From a6b638029e8bd791e092b71f163325e04bb27017 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C3=89tienne=20Mollier?= Date: Thu, 18 Dec 2025 22:30:06 +0100 Subject: [PATCH] New upstream version 3.7.0 --- .github/workflows/cmake-win.yml | 10 +- .gitignore | 6 + ANNOUNCE | 183 +- CMake/3rdparty.cmake | 18 +- CMake/CTest/CTestCustomAndroid.cmake.in | 12 +- CMake/CTest/dcmtkCTestRunAndroid.cmake.in | 2 +- CMake/DCMTKConfig.cmake.in | 15 +- CMake/GenerateDCMTKConfigure.cmake | 152 +- CMake/dcmtkMacros.cmake | 3 + CMake/dcmtkPrepare.cmake | 78 +- CMake/dcmtkUseAndroidSDK.cmake | 120 +- CMake/osconfig.h.in | 351 +- CMakeLists.txt | 3 +- COPYRIGHT | 38 +- CREDITS | 12 +- INSTALL | 210 +- Makefile | 338 +- VERSION | 2 +- config/aclocal.m4 | 192 -- config/configure | 1647 +-------- config/configure.in | 112 +- config/confmod | 12 +- config/docs/macros.txt | 17 - config/include/dcmtk/config/osconfig.h.in | 116 - config/math.cc | 23 +- config/modules | 2 +- dcmapps/apps/CMakeLists.txt | 2 +- dcmapps/docs/dcm2img.man | 18 +- dcmapps/include/dcmtk/dcmapps/dcm2img.h | 117 +- dcmdata/apps/CMakeLists.txt | 11 +- dcmdata/apps/Makefile.dep | 557 +-- dcmdata/apps/Makefile.in | 15 +- dcmdata/apps/cda2dcm.cc | 150 +- dcmdata/apps/dcm2cda.cc | 329 +- dcmdata/apps/dcm2json.cc | 321 +- dcmdata/apps/dcm2pdf.cc | 341 +- dcmdata/apps/dcm2xml.cc | 50 +- dcmdata/apps/dcmdecap.cc | 296 ++ dcmdata/apps/dcmencap.cc | 113 + dcmdata/apps/dump2dcm.cc | 5 +- dcmdata/apps/img2dcm.cc | 5 +- dcmdata/apps/json2dcm.cc | 493 +++ dcmdata/apps/mdfdsman.cc | 6 +- dcmdata/apps/pdf2dcm.cc | 135 +- dcmdata/apps/stl2dcm.cc | 137 +- dcmdata/data/dicom.dic | 69 +- dcmdata/docs/cda2dcm.man | 264 +- dcmdata/docs/dcm2cda.man | 199 +- dcmdata/docs/dcm2json.man | 77 +- dcmdata/docs/dcm2pdf.man | 221 +- dcmdata/docs/dcm2xml.man | 2 +- dcmdata/docs/dcmconv.man | 2 +- dcmdata/docs/dcmcrle.man | 2 +- dcmdata/docs/dcmdata.dox | 3 + dcmdata/docs/dcmdecap.man | 240 ++ dcmdata/docs/dcmdrle.man | 2 +- dcmdata/docs/dcmdump.man | 2 +- dcmdata/docs/dcmencap.man | 320 ++ dcmdata/docs/dcmftest.man | 2 +- dcmdata/docs/dcmgpdir.man | 2 +- dcmdata/docs/dcmodify.man | 2 +- dcmdata/docs/dump2dcm.man | 2 +- dcmdata/docs/img2dcm.man | 4 +- dcmdata/docs/json2dcm.man | 481 +++ dcmdata/docs/pdf2dcm.man | 256 +- dcmdata/docs/stl2dcm.man | 274 +- dcmdata/docs/xml2dcm.man | 2 +- dcmdata/include/dcmtk/dcmdata/dcbytstr.h | 6 +- dcmdata/include/dcmtk/dcmdata/dccodec.h | 30 +- dcmdata/include/dcmtk/dcmdata/dcdatset.h | 11 +- dcmdata/include/dcmtk/dcmdata/dcddirif.h | 15 +- dcmdata/include/dcmtk/dcmdata/dcdeftag.h | 68 +- dcmdata/include/dcmtk/dcmdata/dcdirrec.h | 6 +- dcmdata/include/dcmtk/dcmdata/dcdocdec.h | 120 + dcmdata/include/dcmtk/dcmdata/dcelem.h | 76 +- dcmdata/include/dcmtk/dcmdata/dcencdoc.h | 508 ++- dcmdata/include/dcmtk/dcmdata/dcerror.h | 31 +- dcmdata/include/dcmtk/dcmdata/dcfilefo.h | 28 +- dcmdata/include/dcmtk/dcmdata/dcitem.h | 210 +- dcmdata/include/dcmtk/dcmdata/dcjson.h | 124 +- dcmdata/include/dcmtk/dcmdata/dcjsonrd.h | 345 ++ dcmdata/include/dcmtk/dcmdata/dclist.h | 59 +- dcmdata/include/dcmtk/dcmdata/dcmetinf.h | 8 +- .../include/dcmtk/dcmdata/dcmxml/xml2dcm.h | 8 +- dcmdata/include/dcmtk/dcmdata/dcpixel.h | 15 + dcmdata/include/dcmtk/dcmdata/dcpixseq.h | 10 +- dcmdata/include/dcmtk/dcmdata/dcrleccd.h | 15 +- dcmdata/include/dcmtk/dcmdata/dcrlecce.h | 15 +- dcmdata/include/dcmtk/dcmdata/dcspchrs.h | 96 +- dcmdata/include/dcmtk/dcmdata/dctypes.h | 18 +- dcmdata/include/dcmtk/dcmdata/dcuid.h | 7 +- dcmdata/include/dcmtk/dcmdata/dcvrae.h | 6 +- dcmdata/include/dcmtk/dcmdata/dcvrobow.h | 18 +- dcmdata/include/dcmtk/dcmdata/dcxfer.h | 94 +- dcmdata/include/dcmtk/dcmdata/libi2d/i2d.h | 12 +- .../include/dcmtk/dcmdata/libi2d/i2djpgs.h | 6 +- .../include/dcmtk/dcmdata/libi2d/i2doutpl.h | 18 +- .../include/dcmtk/dcmdata/libi2d/i2dplnsc.h | 17 +- .../include/dcmtk/dcmdata/libi2d/i2dplop.h | 17 +- .../include/dcmtk/dcmdata/libi2d/i2dplsc.h | 17 +- .../include/dcmtk/dcmdata/libi2d/i2dplvlp.h | 17 +- dcmdata/libdcxml/Makefile.dep | 59 +- dcmdata/libdcxml/xml2dcm.cc | 26 +- dcmdata/libi2d/Makefile.dep | 70 +- dcmdata/libi2d/i2d.cc | 53 +- dcmdata/libi2d/i2djpgs.cc | 104 +- dcmdata/libi2d/i2dplnsc.cc | 147 +- dcmdata/libi2d/i2dplop.cc | 142 +- dcmdata/libi2d/i2dplsc.cc | 15 +- dcmdata/libi2d/i2dplvlp.cc | 142 +- dcmdata/libsrc/CMakeLists.txt | 2 + dcmdata/libsrc/Makefile.dep | 163 +- dcmdata/libsrc/Makefile.in | 2 +- dcmdata/libsrc/cmdlnarg.cc | 4 +- dcmdata/libsrc/dcbytstr.cc | 34 +- dcmdata/libsrc/dcchrstr.cc | 20 +- dcmdata/libsrc/dccodec.cc | 36 +- dcmdata/libsrc/dcdatset.cc | 19 +- dcmdata/libsrc/dcddirif.cc | 64 +- dcmdata/libsrc/dcdict.cc | 4 +- dcmdata/libsrc/dcdictbi.cc | 244 +- dcmdata/libsrc/dcdirrec.cc | 7 +- dcmdata/libsrc/dcdocdec.cc | 245 ++ dcmdata/libsrc/dcelem.cc | 259 +- dcmdata/libsrc/dcencdoc.cc | 2408 +++++++------ dcmdata/libsrc/dcerror.cc | 17 +- dcmdata/libsrc/dcfilefo.cc | 102 +- dcmdata/libsrc/dcistrmf.cc | 4 +- dcmdata/libsrc/dcistrms.cc | 4 +- dcmdata/libsrc/dcistrmz.cc | 6 +- dcmdata/libsrc/dcitem.cc | 296 +- dcmdata/libsrc/dcjson.cc | 216 +- dcmdata/libsrc/dcjsonrd.cc | 1616 +++++++++ dcmdata/libsrc/dclist.cc | 217 +- dcmdata/libsrc/dcmetinf.cc | 9 +- dcmdata/libsrc/dcostrmf.cc | 4 +- dcmdata/libsrc/dcostrms.cc | 4 +- dcmdata/libsrc/dcpixel.cc | 98 +- dcmdata/libsrc/dcpixseq.cc | 100 +- dcmdata/libsrc/dcrleccd.cc | 36 +- dcmdata/libsrc/dcrlecce.cc | 8 + dcmdata/libsrc/dcspchrs.cc | 589 ++-- dcmdata/libsrc/dcuid.cc | 27 +- dcmdata/libsrc/dcvrds.cc | 61 +- dcmdata/libsrc/dcvrfd.cc | 18 +- dcmdata/libsrc/dcvrfl.cc | 19 +- dcmdata/libsrc/dcvris.cc | 57 +- dcmdata/libsrc/dcvrobow.cc | 55 +- dcmdata/libsrc/dcvrod.cc | 56 +- dcmdata/libsrc/dcvrof.cc | 58 +- dcmdata/libsrc/dcvrol.cc | 29 +- dcmdata/libsrc/dcvrov.cc | 29 +- dcmdata/libsrc/dcvrpobw.cc | 4 +- dcmdata/libsrc/dcvrsv.cc | 17 +- dcmdata/libsrc/dcvruv.cc | 17 +- dcmdata/libsrc/dcxfer.cc | 341 +- dcmdata/libsrc/vrscanl.c | 246 +- dcmdata/libsrc/vrscanl.l | 4 +- dcmdata/tests/CMakeLists.txt | 3 +- dcmdata/tests/Makefile.dep | 66 + dcmdata/tests/Makefile.in | 2 +- dcmdata/tests/tchval.cc | 24 +- dcmdata/tests/tests.cc | 4 +- dcmdata/tests/tfilter.cc | 5 +- dcmdata/tests/tfrmsiz.cc | 138 + dcmdata/tests/tsequen.cc | 22 +- dcmdata/tests/tvrdatim.cc | 2 +- dcmect/include/dcmtk/dcmect/enhanced_ct.h | 14 +- dcmect/libsrc/Makefile.dep | 1 + dcmect/libsrc/enhanced_ct.cc | 67 +- dcmect/tests/CMakeLists.txt | 2 +- dcmect/tests/Makefile.dep | 3 + dcmect/tests/t_huge_concat.cc | 4 +- dcmect/tests/t_overflow.cc | 5 +- .../dcmtk/dcmfg/concatenationcreator.h | 27 +- .../include/dcmtk/dcmfg/concatenationloader.h | 14 +- dcmfg/include/dcmtk/dcmfg/fg.h | 7 +- dcmfg/include/dcmtk/dcmfg/fgderimg.h | 21 +- dcmfg/include/dcmtk/dcmfg/fginterface.h | 191 +- dcmfg/include/dcmtk/dcmfg/fgseg.h | 4 +- dcmfg/include/dcmtk/dcmfg/fgtypes.h | 11 +- dcmfg/include/dcmtk/dcmfg/framesorter.h | 417 +++ dcmfg/libsrc/Makefile.dep | 49 +- dcmfg/libsrc/concatenationcreator.cc | 97 +- dcmfg/libsrc/concatenationloader.cc | 56 +- dcmfg/libsrc/fg.cc | 7 +- dcmfg/libsrc/fgderimg.cc | 33 +- dcmfg/libsrc/fginterface.cc | 500 ++- dcmfg/libsrc/fgseg.cc | 8 +- dcmfg/libsrc/fgtypes.cc | 3 +- dcmfg/tests/CMakeLists.txt | 2 +- dcmfg/tests/Makefile.dep | 61 +- dcmfg/tests/t_concatenation_loader.cc | 328 +- dcmfg/tests/t_fg_base.cc | 3 +- dcmimage/docs/dcm2pnm.man | 2 +- dcmimage/docs/dcmicmp.man | 2 +- dcmimage/docs/dcmquant.man | 2 +- dcmimage/docs/dcmscale.man | 2 +- dcmimage/include/dcmtk/dcmimage/dicomot.h | 4 +- dcmimage/include/dcmtk/dcmimage/dicopxt.h | 65 +- dcmimage/include/dcmtk/dcmimage/diqtpbox.h | 4 +- dcmimage/include/dcmtk/dcmimage/diybrpxt.h | 292 +- dcmimage/libsrc/Makefile.dep | 41 +- dcmimage/libsrc/dipitiff.cc | 49 +- dcmimage/libsrc/diqtctab.cc | 23 +- dcmimgle/docs/dcmdspfn.man | 2 +- dcmimgle/docs/dcod2lum.man | 2 +- dcmimgle/docs/dconvlum.man | 2 +- dcmimgle/include/dcmtk/dcmimgle/diinpxt.h | 100 +- dcmimgle/include/dcmtk/dcmimgle/diluptab.h | 51 +- dcmimgle/include/dcmtk/dcmimgle/dimocpt.h | 4 +- dcmimgle/include/dcmtk/dcmimgle/dimoflt.h | 4 +- dcmimgle/include/dcmtk/dcmimgle/dimoipxt.h | 24 +- dcmimgle/include/dcmtk/dcmimgle/dimopxt.h | 20 +- dcmimgle/include/dcmtk/dcmimgle/dimorot.h | 4 +- dcmimgle/include/dcmtk/dcmimgle/dimosct.h | 4 +- dcmimgle/include/dcmtk/dcmimgle/diutils.h | 8 +- dcmimgle/libsrc/Makefile.dep | 92 +- dcmimgle/libsrc/dcmimage.cc | 3 +- dcmimgle/libsrc/dicielut.cc | 6 +- dcmimgle/libsrc/didocu.cc | 9 +- dcmimgle/libsrc/digsdlut.cc | 6 +- dcmimgle/libsrc/diimage.cc | 18 +- dcmimgle/libsrc/diluptab.cc | 117 +- dcmimgle/libsrc/dimoimg.cc | 17 +- dcmiod/include/dcmtk/dcmiod/iccexample.h | 96 + dcmiod/include/dcmtk/dcmiod/iodimage.h | 17 +- dcmiod/include/dcmtk/dcmiod/iodrules.h | 13 +- dcmiod/include/dcmtk/dcmiod/iodtypes.h | 226 +- dcmiod/include/dcmtk/dcmiod/iodutil.h | 47 +- dcmiod/include/dcmtk/dcmiod/modbase.h | 4 +- dcmiod/include/dcmtk/dcmiod/modequipment.h | 8 +- dcmiod/include/dcmtk/dcmiod/modgeneralimage.h | 4 +- dcmiod/include/dcmtk/dcmiod/modiccprofile.h | 132 + .../dcmtk/dcmiod/modimagepixelvariant.h | 16 +- .../include/dcmtk/dcmiod/modpalettecolorlut.h | 523 +++ dcmiod/libsrc/CMakeLists.txt | 4 +- dcmiod/libsrc/Makefile.dep | 232 +- dcmiod/libsrc/Makefile.in | 5 +- dcmiod/libsrc/iodmacro.cc | 23 +- dcmiod/libsrc/iodrules.cc | 55 +- dcmiod/libsrc/iodtypes.cc | 3 +- dcmiod/libsrc/iodutil.cc | 47 +- dcmiod/libsrc/modequipment.cc | 12 +- dcmiod/libsrc/modhelp.cc | 4 +- dcmiod/libsrc/modiccprofile.cc | 152 + dcmiod/libsrc/modmultiframedimension.cc | 7 +- dcmiod/libsrc/modpalettecolorlut.cc | 985 ++++++ dcmiod/tests/CMakeLists.txt | 5 +- dcmiod/tests/Makefile.dep | 240 +- dcmiod/tests/Makefile.in | 10 +- dcmiod/tests/tchecks.cc | 4 +- dcmiod/tests/tests.cc | 7 +- dcmiod/tests/ticcprofile.cc | 117 + dcmiod/tests/tmacro.cc | 66 + dcmiod/tests/tpalette.cc | 419 +++ dcmjpeg/apps/dcmdjpeg.cc | 15 +- dcmjpeg/docs/dcmcjpeg.man | 2 +- dcmjpeg/docs/dcmdjpeg.man | 17 +- dcmjpeg/docs/dcmj2pnm.man | 2 +- dcmjpeg/docs/dcmmkdir.man | 2 +- dcmjpeg/include/dcmtk/dcmjpeg/djcodecd.h | 15 +- dcmjpeg/include/dcmtk/dcmjpeg/djcodece.h | 15 +- dcmjpeg/include/dcmtk/dcmjpeg/djcparam.h | 18 +- dcmjpeg/include/dcmtk/dcmjpeg/djdecode.h | 6 +- dcmjpeg/libijg12/jconfig12.h | 12 +- dcmjpeg/libijg12/jerror.c | 11 - dcmjpeg/libijg16/jconfig16.h | 12 +- dcmjpeg/libijg16/jerror.c | 11 - dcmjpeg/libijg8/jconfig8.h | 12 +- dcmjpeg/libijg8/jerror.c | 11 - dcmjpeg/libsrc/Makefile.dep | 1 + dcmjpeg/libsrc/djcodecd.cc | 29 +- dcmjpeg/libsrc/djcodece.cc | 8 + dcmjpeg/libsrc/djcparam.cc | 7 +- dcmjpeg/libsrc/djdecode.cc | 12 +- dcmjpls/apps/CMakeLists.txt | 2 +- dcmjpls/docs/dcmcjpls.man | 2 +- dcmjpls/docs/dcmdjpls.man | 2 +- dcmjpls/docs/dcml2pnm.man | 2 +- dcmjpls/include/dcmtk/dcmjpls/djcodecd.h | 54 +- dcmjpls/include/dcmtk/dcmjpls/djcodece.h | 15 +- dcmjpls/libcharls/encodstr.h | 38 +- dcmjpls/libcharls/lltraits.h | 5 +- dcmjpls/libcharls/scan.h | 164 +- dcmjpls/libcharls/streams.h | 240 +- dcmjpls/libsrc/djcodecd.cc | 72 +- dcmjpls/libsrc/djcodece.cc | 58 +- dcmnet/apps/CMakeLists.txt | 6 +- dcmnet/apps/Makefile.dep | 7 +- dcmnet/apps/Makefile.in | 2 +- dcmnet/apps/dcmrecv.cc | 20 +- dcmnet/apps/echoscu.cc | 3 +- dcmnet/apps/movescu.cc | 107 +- dcmnet/apps/storescp.cc | 229 +- dcmnet/docs/dcmrecv.man | 13 +- dcmnet/docs/dcmsend.man | 2 +- dcmnet/docs/echoscu.man | 4 +- dcmnet/docs/findscu.man | 2 +- dcmnet/docs/getscu.man | 4 +- dcmnet/docs/movescu.man | 142 +- dcmnet/docs/storescp.man | 20 +- dcmnet/docs/storescu.man | 2 +- dcmnet/docs/termscu.man | 2 +- dcmnet/etc/storescp.cfg | 89 +- dcmnet/etc/storescu.cfg | 6 +- dcmnet/include/dcmtk/dcmnet/assoc.h | 15 +- dcmnet/include/dcmtk/dcmnet/dcompat.h | 18 +- dcmnet/include/dcmtk/dcmnet/dul.h | 33 +- dcmnet/include/dcmtk/dcmnet/scp.h | 4 +- dcmnet/include/dcmtk/dcmnet/scpcfg.h | 17 +- dcmnet/include/dcmtk/dcmnet/scppool.h | 20 +- dcmnet/include/dcmtk/dcmnet/scpthrd.h | 11 +- dcmnet/include/dcmtk/dcmnet/scu.h | 10 +- dcmnet/libsrc/assoc.cc | 38 +- dcmnet/libsrc/dcmtrans.cc | 29 +- dcmnet/libsrc/dcompat.cc | 30 +- dcmnet/libsrc/dimcancl.cc | 5 +- dcmnet/libsrc/dimcmd.cc | 5 +- dcmnet/libsrc/dimdump.cc | 5 +- dcmnet/libsrc/dimfind.cc | 5 +- dcmnet/libsrc/dimget.cc | 5 +- dcmnet/libsrc/dimmove.cc | 6 +- dcmnet/libsrc/dimse.cc | 6 +- dcmnet/libsrc/dimstore.cc | 5 +- dcmnet/libsrc/diutil.cc | 7 +- dcmnet/libsrc/dstorscp.cc | 3 +- dcmnet/libsrc/dul.cc | 330 +- dcmnet/libsrc/dulextra.cc | 6 +- dcmnet/libsrc/dulfsm.cc | 14 +- dcmnet/libsrc/scp.cc | 20 +- dcmnet/libsrc/scpcfg.cc | 17 +- dcmnet/libsrc/scppool.cc | 134 +- dcmnet/libsrc/scpthrd.cc | 5 + dcmnet/libsrc/scu.cc | 9 +- dcmnet/tests/tpool.cc | 72 +- .../dcmtk/dcmpmap/dpmparametricmapiod.h | 33 +- dcmpmap/libsrc/Makefile.dep | 4 + dcmpmap/libsrc/dpmparametricmapiod.cc | 68 +- dcmpstat/apps/CMakeLists.txt | 4 +- dcmpstat/apps/Makefile.dep | 7 +- dcmpstat/apps/dcmprscp.cc | 6 +- dcmpstat/apps/dcmprscu.cc | 4 +- dcmpstat/apps/dcmpschk.cc | 16 +- dcmpstat/apps/dcmpsmk.cc | 15 +- dcmpstat/apps/dcmpsrcv.cc | 24 +- dcmpstat/apps/dcmpssnd.cc | 12 +- dcmpstat/docs/dcmmkcrv.man | 2 +- dcmpstat/docs/dcmmklut.man | 2 +- dcmpstat/docs/dcmp2pgm.man | 2 +- dcmpstat/docs/dcmprscp.man | 5 +- dcmpstat/docs/dcmprscu.man | 5 +- dcmpstat/docs/dcmpschk.man | 16 +- dcmpstat/docs/dcmpsmk.man | 16 +- dcmpstat/docs/dcmpsprt.man | 2 +- dcmpstat/docs/dcmpsrcv.man | 2 +- dcmpstat/docs/dcmpssnd.man | 2 +- dcmpstat/etc/dcmpstat.cfg | 30 +- dcmpstat/include/dcmtk/dcmpstat/dviface.h | 2 +- dcmpstat/include/dcmtk/dcmpstat/dvpscf.h | 40 +- dcmpstat/include/dcmtk/dcmpstat/dvpsri.h | 34 +- dcmpstat/libsrc/Makefile.dep | 6 +- dcmpstat/libsrc/dviface.cc | 32 +- dcmpstat/libsrc/dvpscf.cc | 27 +- dcmpstat/libsrc/dvpshlp.cc | 4 +- dcmpstat/libsrc/dvpsmsg.cc | 7 +- dcmpstat/libsrc/dvpsri.cc | 79 +- dcmpstat/tests/CMakeLists.txt | 2 +- dcmpstat/tests/msgserv.cc | 4 +- dcmqrdb/apps/dcmqrscp.cc | 18 +- dcmqrdb/docs/dcmqridx.man | 2 +- dcmqrdb/docs/dcmqrscp.man | 20 +- dcmqrdb/docs/dcmqrti.man | 2 +- dcmqrdb/etc/dcmqrprf.cfg | 139 +- dcmqrdb/include/dcmtk/dcmqrdb/dcmqrcbm.h | 9 +- dcmqrdb/include/dcmtk/dcmqrdb/dcmqropt.h | 6 +- dcmqrdb/libsrc/dcmqrcbg.cc | 8 +- dcmqrdb/libsrc/dcmqrcbm.cc | 26 +- dcmqrdb/libsrc/dcmqrcnf.cc | 9 +- dcmqrdb/libsrc/dcmqrdbi.cc | 22 +- dcmqrdb/libsrc/dcmqropt.cc | 3 +- dcmqrdb/libsrc/dcmqrsrv.cc | 5 +- dcmqrdb/libsrc/dcmqrtis.cc | 10 +- dcmrt/apps/CMakeLists.txt | 2 +- dcmrt/docs/drtdump.man | 2 +- dcmrt/include/dcmtk/dcmrt/drttypes.h | 6 +- dcmrt/libsrc/drtdose.cc | 2 +- dcmrt/libsrc/drtimage.cc | 2 +- dcmrt/libsrc/drtionpl.cc | 2 +- dcmrt/libsrc/drtiontr.cc | 2 +- dcmrt/libsrc/drtplan.cc | 2 +- dcmrt/libsrc/drtstrct.cc | 4 +- dcmrt/libsrc/drttreat.cc | 2 +- dcmrt/tests/CMakeLists.txt | 4 +- dcmseg/Makefile.in | 7 +- dcmseg/include/dcmtk/dcmseg/overlaputil.h | 379 +++ dcmseg/include/dcmtk/dcmseg/segdoc.h | 509 ++- dcmseg/include/dcmtk/dcmseg/segment.h | 57 +- dcmseg/include/dcmtk/dcmseg/segtypes.h | 86 +- dcmseg/include/dcmtk/dcmseg/segutils.h | 87 +- dcmseg/libsrc/CMakeLists.txt | 1 + dcmseg/libsrc/Makefile.dep | 221 +- dcmseg/libsrc/Makefile.in | 2 +- dcmseg/libsrc/overlaputil.cc | 833 +++++ dcmseg/libsrc/segdoc.cc | 1202 +++++-- dcmseg/libsrc/segment.cc | 109 +- dcmseg/libsrc/segtypes.cc | 92 +- dcmseg/libsrc/segutils.cc | 123 +- dcmseg/tests/CMakeLists.txt | 5 +- dcmseg/tests/Makefile.dep | 343 +- dcmseg/tests/Makefile.in | 13 +- dcmseg/tests/tbigdim.cc | 34 +- dcmseg/tests/tconcat_binary.cc | 6 +- dcmseg/tests/tests.cc | 7 +- dcmseg/tests/tlabelmap.cc | 676 ++++ dcmseg/tests/tpacking.cc | 227 ++ dcmseg/tests/troundtrip.cc | 25 +- dcmseg/tests/tutils.cc | 81 +- dcmsign/docs/dcmsign.man | 2 +- dcmsr/apps/Makefile.dep | 16 +- dcmsr/apps/dsr2html.cc | 4 +- dcmsr/apps/dsrdump.cc | 2 +- dcmsr/apps/xml2dsr.cc | 9 +- dcmsr/data/dsr2xml.xsd | 11 +- dcmsr/docs/dsr2html.man | 16 +- dcmsr/docs/dsr2xml.man | 6 +- dcmsr/docs/dsrdump.man | 6 +- dcmsr/docs/xml2dsr.man | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid100.h | 12 +- dcmsr/include/dcmtk/dcmsr/cmr/cid10013.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid10033.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid11.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid218.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid244.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid247.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid29.h | 10 +- dcmsr/include/dcmtk/dcmsr/cmr/cid4020.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid4021.h | 34 +- dcmsr/include/dcmtk/dcmsr/cmr/cid4031.h | 30 +- dcmsr/include/dcmtk/dcmsr/cmr/cid42.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid6147.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid7021.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid7181.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid7445.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid7452.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid7453.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid7464.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid7469.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/cid7551.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/tid1500.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/tid15def.h | 6 +- dcmsr/include/dcmtk/dcmsr/cmr/tid1600.h | 6 +- dcmsr/include/dcmtk/dcmsr/codes/dcm.h | 345 +- dcmsr/include/dcmtk/dcmsr/codes/ncit.h | 18 +- dcmsr/include/dcmtk/dcmsr/codes/umls.h | 17 +- dcmsr/include/dcmtk/dcmsr/dsrcomvl.h | 6 +- dcmsr/include/dcmtk/dcmsr/dsrctxgr.h | 14 +- dcmsr/include/dcmtk/dcmsr/dsrdoc.h | 29 +- dcmsr/include/dcmtk/dcmsr/dsrdoctn.h | 6 +- dcmsr/include/dcmtk/dcmsr/dsrdoctr.h | 5 +- dcmsr/include/dcmtk/dcmsr/dsrimgvl.h | 21 +- dcmsr/include/dcmtk/dcmsr/dsrtypes.h | 36 +- dcmsr/include/dcmtk/dcmsr/dsrwavvl.h | 6 +- dcmsr/include/dcmtk/dcmsr/dsrxmlc.h | 11 +- dcmsr/libcmr/cid100.cc | 14 +- dcmsr/libcmr/cid10013.cc | 11 +- dcmsr/libcmr/cid10033.cc | 11 +- dcmsr/libcmr/cid11.cc | 11 +- dcmsr/libcmr/cid218.cc | 11 +- dcmsr/libcmr/cid244.cc | 11 +- dcmsr/libcmr/cid247.cc | 11 +- dcmsr/libcmr/cid29.cc | 13 +- dcmsr/libcmr/cid4020.cc | 11 +- dcmsr/libcmr/cid4021.cc | 26 +- dcmsr/libcmr/cid4031.cc | 24 +- dcmsr/libcmr/cid42.cc | 11 +- dcmsr/libcmr/cid5000.cc | 7 +- dcmsr/libcmr/cid5001.cc | 7 +- dcmsr/libcmr/cid6147.cc | 11 +- dcmsr/libcmr/cid7021.cc | 11 +- dcmsr/libcmr/cid7181.cc | 11 +- dcmsr/libcmr/cid7445.cc | 11 +- dcmsr/libcmr/cid7452.cc | 11 +- dcmsr/libcmr/cid7453.cc | 11 +- dcmsr/libcmr/cid7464.cc | 11 +- dcmsr/libcmr/cid7469.cc | 11 +- dcmsr/libcmr/cid7551.cc | 11 +- dcmsr/libsrc/Makefile.dep | 7 +- dcmsr/libsrc/dsrctxgr.cc | 5 +- dcmsr/libsrc/dsrdoc.cc | 36 +- dcmsr/libsrc/dsrdoctn.cc | 83 +- dcmsr/libsrc/dsrimgvl.cc | 31 +- dcmsr/libsrc/dsrnumvl.cc | 11 +- dcmsr/libsrc/dsrtypes.cc | 6 +- dcmsr/libsrc/dsrxmld.cc | 24 +- dcmsr/tests/CMakeLists.txt | 1 + dcmsr/tests/Makefile.dep | 95 +- dcmsr/tests/Makefile.in | 4 +- dcmsr/tests/tests.cc | 4 +- dcmsr/tests/tsrcmr.cc | 8 +- dcmsr/tests/tsrdoc.cc | 5 +- dcmsr/tests/tsrimgvl.cc | 122 + dcmtls/include/dcmtk/dcmtls/tlsscu.h | 8 +- dcmtls/libsrc/tlsciphr.cc | 4 +- dcmtls/libsrc/tlsfmacr.h | 4 +- dcmtls/libsrc/tlsscu.cc | 11 +- dcmtls/libsrc/tlstrans.cc | 4 +- dcmtls/tests/CMakeLists.txt | 2 +- dcmtls/tests/tscuscptls.cc | 39 +- dcmtract/CMakeLists.txt | 2 +- dcmtract/include/dcmtk/dcmtract/trctrackset.h | 3 +- dcmtract/include/dcmtk/dcmtract/trctypes.h | 4 +- dcmtract/libsrc/Makefile.dep | 6 + dcmtract/libsrc/trcmodtractresults.cc | 18 +- dcmtract/libsrc/trctrackset.cc | 17 +- dcmtract/libsrc/trctractographyresults.cc | 6 +- dcmtract/libsrc/trctypes.cc | 17 +- dcmtract/tests/CMakeLists.txt | 11 + dcmtract/tests/Makefile.dep | 159 + dcmtract/tests/Makefile.in | 45 +- dcmtract/tests/tcreate.cc | 132 + dcmtract/tests/tests.cc | 28 + dcmwlm/apps/CMakeLists.txt | 2 +- dcmwlm/apps/wlcefs.cc | 15 +- dcmwlm/docs/wlmscpfs.man | 15 +- dcmwlm/include/dcmtk/dcmwlm/wlds.h | 4 +- dcmwlm/libsrc/wlds.cc | 6 +- dcmwlm/libsrc/wldsfs.cc | 4 +- dcmwlm/tests/CMakeLists.txt | 2 +- docs/ANNOUNCE.369 | 168 + docs/CHANGES.370 | 3027 +++++++++++++++++ doxygen/manpages/man1/cda2dcm.1 | 221 +- doxygen/manpages/man1/dcm2cda.1 | 161 +- doxygen/manpages/man1/dcm2img.1 | 20 +- doxygen/manpages/man1/dcm2json.1 | 53 +- doxygen/manpages/man1/dcm2pdf.1 | 180 +- doxygen/manpages/man1/dcm2pnm.1 | 4 +- doxygen/manpages/man1/dcm2xml.1 | 4 +- doxygen/manpages/man1/dcmcjpeg.1 | 4 +- doxygen/manpages/man1/dcmcjpls.1 | 4 +- doxygen/manpages/man1/dcmconv.1 | 4 +- doxygen/manpages/man1/dcmcrle.1 | 4 +- doxygen/manpages/man1/dcmdecap.1 | 192 ++ doxygen/manpages/man1/dcmdjpeg.1 | 19 +- doxygen/manpages/man1/dcmdjpls.1 | 4 +- doxygen/manpages/man1/dcmdrle.1 | 4 +- doxygen/manpages/man1/dcmdspfn.1 | 4 +- doxygen/manpages/man1/dcmdump.1 | 4 +- doxygen/manpages/man1/dcmencap.1 | 271 ++ doxygen/manpages/man1/dcmftest.1 | 4 +- doxygen/manpages/man1/dcmgpdir.1 | 4 +- doxygen/manpages/man1/dcmicmp.1 | 4 +- doxygen/manpages/man1/dcmj2pnm.1 | 4 +- doxygen/manpages/man1/dcml2pnm.1 | 4 +- doxygen/manpages/man1/dcmmkcrv.1 | 4 +- doxygen/manpages/man1/dcmmkdir.1 | 4 +- doxygen/manpages/man1/dcmmklut.1 | 4 +- doxygen/manpages/man1/dcmodify.1 | 4 +- doxygen/manpages/man1/dcmp2pgm.1 | 4 +- doxygen/manpages/man1/dcmprscp.1 | 4 +- doxygen/manpages/man1/dcmprscu.1 | 4 +- doxygen/manpages/man1/dcmpschk.1 | 19 +- doxygen/manpages/man1/dcmpsmk.1 | 19 +- doxygen/manpages/man1/dcmpsprt.1 | 4 +- doxygen/manpages/man1/dcmpsrcv.1 | 4 +- doxygen/manpages/man1/dcmpssnd.1 | 4 +- doxygen/manpages/man1/dcmqridx.1 | 4 +- doxygen/manpages/man1/dcmqrscp.1 | 22 +- doxygen/manpages/man1/dcmqrti.1 | 4 +- doxygen/manpages/man1/dcmquant.1 | 4 +- doxygen/manpages/man1/dcmrecv.1 | 15 +- doxygen/manpages/man1/dcmscale.1 | 4 +- doxygen/manpages/man1/dcmsend.1 | 4 +- doxygen/manpages/man1/dcmsign.1 | 4 +- doxygen/manpages/man1/dcod2lum.1 | 4 +- doxygen/manpages/man1/dconvlum.1 | 4 +- doxygen/manpages/man1/drtdump.1 | 4 +- doxygen/manpages/man1/dsr2html.1 | 14 +- doxygen/manpages/man1/dsr2xml.1 | 8 +- doxygen/manpages/man1/dsrdump.1 | 8 +- doxygen/manpages/man1/dump2dcm.1 | 4 +- doxygen/manpages/man1/echoscu.1 | 6 +- doxygen/manpages/man1/findscu.1 | 4 +- doxygen/manpages/man1/getscu.1 | 6 +- doxygen/manpages/man1/img2dcm.1 | 6 +- doxygen/manpages/man1/json2dcm.1 | 398 +++ doxygen/manpages/man1/mkcsmapper.1 | 2 +- doxygen/manpages/man1/mkesdb.1 | 2 +- doxygen/manpages/man1/movescu.1 | 145 +- doxygen/manpages/man1/pdf2dcm.1 | 212 +- doxygen/manpages/man1/stl2dcm.1 | 231 +- doxygen/manpages/man1/storescp.1 | 22 +- doxygen/manpages/man1/storescu.1 | 4 +- doxygen/manpages/man1/termscu.1 | 4 +- doxygen/manpages/man1/wlmscpfs.1 | 17 +- doxygen/manpages/man1/xml2dcm.1 | 4 +- doxygen/manpages/man1/xml2dsr.1 | 8 +- oficonv/apps/mkcsmapper.y | 4 +- oficonv/apps/mkcsmapper_bison.cc | 216 +- oficonv/apps/mkcsmapper_bison.h | 8 +- oficonv/apps/mkesdb.y | 2 - oficonv/apps/mkesdb_bison.cc | 135 +- oficonv/apps/mkesdb_bison.h | 8 +- oficonv/docs/oficonv.dox | 4 + oficonv/include/dcmtk/oficonv/iconv.h | 11 +- oficonv/libsrc/Makefile.dep | 5 +- oficonv/libsrc/citrus_bcs.c | 16 + oficonv/libsrc/citrus_bcs.h | 2 - oficonv/libsrc/citrus_big5.c | 4 - oficonv/libsrc/citrus_csmapper.c | 2 - oficonv/libsrc/citrus_db.c | 4 - oficonv/libsrc/citrus_db_factory.c | 2 - oficonv/libsrc/citrus_db_hash.c | 4 - oficonv/libsrc/citrus_dechanyu.c | 3 - oficonv/libsrc/citrus_esdb.c | 4 - oficonv/libsrc/citrus_euc.c | 4 - oficonv/libsrc/citrus_euctw.c | 4 - oficonv/libsrc/citrus_gbk2k.c | 4 - oficonv/libsrc/citrus_hash.c | 4 - oficonv/libsrc/citrus_hz.c | 2 - oficonv/libsrc/citrus_iconv.c | 6 +- oficonv/libsrc/citrus_iconv.h | 9 +- oficonv/libsrc/citrus_iconv_local.h | 6 +- oficonv/libsrc/citrus_iconv_none.c | 4 +- oficonv/libsrc/citrus_iconv_std.c | 12 +- oficonv/libsrc/citrus_iso2022.c | 4 - oficonv/libsrc/citrus_jisx0208.c | 4 - oficonv/libsrc/citrus_johab.c | 4 - oficonv/libsrc/citrus_lock.h | 18 +- oficonv/libsrc/citrus_lookup.c | 4 - oficonv/libsrc/citrus_mapper.c | 13 +- oficonv/libsrc/citrus_mapper_zone.c | 3 +- oficonv/libsrc/citrus_mmap.c | 6 - oficonv/libsrc/citrus_module.c | 4 - oficonv/libsrc/citrus_mskanji.c | 4 - oficonv/libsrc/citrus_none.c | 4 - oficonv/libsrc/citrus_region.h | 3 - oficonv/libsrc/citrus_types.h | 2 - oficonv/libsrc/citrus_utf1632.c | 4 - oficonv/libsrc/citrus_utf8.c | 4 - oficonv/libsrc/citrus_viqr.c | 4 - oficonv/libsrc/citrus_zw.c | 4 - oficonv/libsrc/oficonv_iconv.c | 24 +- oficonv/libsrc/oficonv_logger.c | 18 +- oficonv/tests/tests.cc | 5 +- oficonv/tests/ticonv.cc | 83 +- oflog/include/dcmtk/oflog/config.h | 7 +- oflog/include/dcmtk/oflog/config/defines.h | 12 - oflog/include/dcmtk/oflog/config/win32.h | 19 +- oflog/libsrc/fileap.cc | 38 +- oflog/libsrc/log4judp.cc | 2 - oflog/libsrc/threads.cc | 2 - oflog/libsrc/timehelp.cc | 6 +- ofstd/include/dcmtk/ofstd/diag/clangprg.def | 7 + ofstd/include/dcmtk/ofstd/diag/vsconstexp.def | 7 + ofstd/include/dcmtk/ofstd/ofchrenc.h | 6 +- ofstd/include/dcmtk/ofstd/ofcmdln.h | 6 +- ofstd/include/dcmtk/ofstd/ofcond.h | 7 +- ofstd/include/dcmtk/ofstd/ofdate.h | 6 +- ofstd/include/dcmtk/ofstd/ofdiag.h | 2 + ofstd/include/dcmtk/ofstd/offile.h | 17 +- ofstd/include/dcmtk/ofstd/ofjsmn.h | 473 +++ ofstd/include/dcmtk/ofstd/oflist.h | 11 +- ofstd/include/dcmtk/ofstd/ofmath.h | 9 +- ofstd/include/dcmtk/ofstd/ofmem.h | 6 +- ofstd/include/dcmtk/ofstd/ofoption.h | 17 +- ofstd/include/dcmtk/ofstd/ofrand.h | 4 +- ofstd/include/dcmtk/ofstd/ofsha256.h | 71 + ofstd/include/dcmtk/ofstd/ofsockad.h | 6 +- ofstd/include/dcmtk/ofstd/ofstd.h | 18 +- ofstd/include/dcmtk/ofstd/ofstdinc.h | 6 +- ofstd/include/dcmtk/ofstd/ofstream.h | 6 +- ofstd/include/dcmtk/ofstd/ofstrhlp.h | 59 + ofstd/include/dcmtk/ofstd/oftempf.h | 6 +- ofstd/include/dcmtk/ofstd/oftest.h | 6 +- ofstd/include/dcmtk/ofstd/oftime.h | 6 +- ofstd/include/dcmtk/ofstd/oftypes.h | 16 +- ofstd/include/dcmtk/ofstd/ofvector.h | 7 +- ofstd/libsrc/CMakeLists.txt | 4 +- ofstd/libsrc/Makefile.dep | 9 + ofstd/libsrc/Makefile.in | 2 +- ofstd/libsrc/ofconsol.cc | 12 +- ofstd/libsrc/ofdatime.cc | 4 +- ofstd/libsrc/offilsys.cc | 8 +- ofstd/libsrc/offname.cc | 6 +- ofstd/libsrc/ofipc.cc | 8 +- ofstd/libsrc/ofjsmn.cc | 22 + ofstd/libsrc/ofmath.cc | 7 +- ofstd/libsrc/ofrand.cc | 4 +- ofstd/libsrc/ofsha256.cc | 169 + ofstd/libsrc/ofstd.cc | 98 +- ofstd/libsrc/ofstub.cc | 41 +- ofstd/libsrc/ofxml.cc | 4 +- ofstd/tests/Makefile.dep | 15 +- ofstd/tests/tests.cc | 3 +- ofstd/tests/tstring.cc | 36 +- 696 files changed, 31489 insertions(+), 13170 deletions(-) create mode 100644 dcmdata/apps/dcmdecap.cc create mode 100644 dcmdata/apps/dcmencap.cc create mode 100644 dcmdata/apps/json2dcm.cc create mode 100644 dcmdata/docs/dcmdecap.man create mode 100644 dcmdata/docs/dcmencap.man create mode 100644 dcmdata/docs/json2dcm.man create mode 100644 dcmdata/include/dcmtk/dcmdata/dcdocdec.h create mode 100644 dcmdata/include/dcmtk/dcmdata/dcjsonrd.h create mode 100644 dcmdata/libsrc/dcdocdec.cc create mode 100644 dcmdata/libsrc/dcjsonrd.cc create mode 100644 dcmdata/tests/tfrmsiz.cc create mode 100644 dcmfg/include/dcmtk/dcmfg/framesorter.h create mode 100644 dcmiod/include/dcmtk/dcmiod/iccexample.h create mode 100644 dcmiod/include/dcmtk/dcmiod/modiccprofile.h create mode 100644 dcmiod/include/dcmtk/dcmiod/modpalettecolorlut.h create mode 100644 dcmiod/libsrc/modiccprofile.cc create mode 100644 dcmiod/libsrc/modpalettecolorlut.cc create mode 100644 dcmiod/tests/ticcprofile.cc create mode 100644 dcmiod/tests/tmacro.cc create mode 100644 dcmiod/tests/tpalette.cc create mode 100644 dcmseg/include/dcmtk/dcmseg/overlaputil.h create mode 100644 dcmseg/libsrc/overlaputil.cc create mode 100644 dcmseg/tests/tlabelmap.cc create mode 100644 dcmseg/tests/tpacking.cc create mode 100644 dcmsr/tests/tsrimgvl.cc create mode 100644 dcmtract/tests/CMakeLists.txt create mode 100644 dcmtract/tests/tcreate.cc create mode 100644 dcmtract/tests/tests.cc create mode 100644 docs/ANNOUNCE.369 create mode 100644 docs/CHANGES.370 create mode 100644 doxygen/manpages/man1/dcmdecap.1 create mode 100644 doxygen/manpages/man1/dcmencap.1 create mode 100644 doxygen/manpages/man1/json2dcm.1 create mode 100644 ofstd/include/dcmtk/ofstd/diag/clangprg.def create mode 100644 ofstd/include/dcmtk/ofstd/diag/vsconstexp.def create mode 100644 ofstd/include/dcmtk/ofstd/ofjsmn.h create mode 100644 ofstd/include/dcmtk/ofstd/ofsha256.h create mode 100644 ofstd/include/dcmtk/ofstd/ofstrhlp.h create mode 100644 ofstd/libsrc/ofjsmn.cc create mode 100644 ofstd/libsrc/ofsha256.cc diff --git a/.github/workflows/cmake-win.yml b/.github/workflows/cmake-win.yml index fd7869c7..a0034b1a 100644 --- a/.github/workflows/cmake-win.yml +++ b/.github/workflows/cmake-win.yml @@ -29,11 +29,11 @@ jobs: - name: Download DCMTK Support libraries shell: pwsh run: | - C:\msys64\usr\bin\wget.exe -O dcmtk_support.zip https://dicom.offis.de/download/dcmtk/dcmtk368/support/dcmtk-3.6.8-win64-support-MD-msvc-17.4.zip + C:\msys64\usr\bin\wget.exe -O dcmtk_support.zip https://dicom.offis.de/download/dcmtk/dcmtk370/support/dcmtk-3.7.0-win64-support-MD-msvc-17.8.zip # Uncompress support libraries into directory c:\dcmtk_support\libs. # We rename the original directory to libs so that the rest of the script - # can use the ame path even if the support library package is updated + # can use the same path even if the support library package is updated # in the future in the download task above. - name: Uncompress Support libraries shell: pwsh @@ -60,7 +60,7 @@ jobs: echo "Step 2" cd ${{ github.workspace }}\dcmtk-build echo "Step 3" - cmake -G "Visual Studio 17 2022" -Ax64 -DDCMTK_MODULES:STR="ofstd;oflog;oficonv;dcmdata;dcmimgle;dcmimage;dcmjpeg;dcmjpls;dcmtls;dcmnet;dcmsr;dcmsign;dcmwlm;dcmqrdb;dcmpstat;dcmrt;dcmiod;dcmfg;dcmseg;dcmtract;dcmpmap;dcmect" -DDCMTK_ENABLE_BUILTIN_OFICONV_DATA:BOOL=On -DBUILD_SHARED_LIBS:BOOL=ON -DDCMTK_SUPPORT_LIBRARIES_DIR:PATH=c:\dcmtk_support\libs -DCMAKE_INSTALL_PREFIX:PATH=${{ github.workspace }}\dcmtk-${{ env.NOW }}-${{ env.COMMIT_SHORT_SHA }} ${{ github.workspace }} + cmake -G "Visual Studio 17 2022" -Ax64 -DDCMTK_MODULES:STR="ofstd;oflog;oficonv;dcmdata;dcmimgle;dcmimage;dcmjpeg;dcmjpls;dcmtls;dcmnet;dcmsr;dcmsign;dcmwlm;dcmqrdb;dcmpstat;dcmrt;dcmiod;dcmfg;dcmseg;dcmtract;dcmpmap;dcmect;dcmapps" -DDCMTK_ENABLE_BUILTIN_OFICONV_DATA:BOOL=On -DBUILD_SHARED_LIBS:BOOL=ON -DDCMTK_SUPPORT_LIBRARIES_DIR:PATH=c:\dcmtk_support\libs -DCMAKE_INSTALL_PREFIX:PATH=${{ github.workspace }}\dcmtk-${{ env.NOW }}-${{ env.COMMIT_SHORT_SHA }} ${{ github.workspace }} - name: Build dcmtk run: | @@ -90,7 +90,7 @@ jobs: runs-on: windows-latest timeout-minutes: 5 - # Only run if the event is not a pull request and the repository owner is DCMTK. + # Only run if the event is not a pull request and the repository owner is michaelonken. # The latter is to prevent forks from publishing packages even if the owner's token # would have sufficient privileges. if: ${{ (github.event_name != 'pull_request') && (github.repository_owner == 'DCMTK')}} @@ -118,4 +118,4 @@ jobs: --prerelease-packages-clear-pattern "dcmtk-*-win64.zip" ` --prerelease-packages-keep-pattern "**" ` --prerelease-sha master ` - --token ${{ secrets.RELEASE_DCMTK_TOKEN }} \ No newline at end of file + --token ${{ secrets.RELEASE_DCMTK_TOKEN }} diff --git a/.gitignore b/.gitignore index 89d3a277..673736bb 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,9 @@ config/autom4te.cache/ *.*~ *.bak +# Ignore caches +.cache + # Also ignore temporary directories _*/ ofstd/tests/tehtestdire/ @@ -45,13 +48,16 @@ dcmdata/apps/dcm2pdf dcmdata/apps/dcm2xml dcmdata/apps/dcmconv dcmdata/apps/dcmcrle +dcmdata/apps/dcmdecap dcmdata/apps/dcmdrle dcmdata/apps/dcmdump +dcmdata/apps/dcmencap dcmdata/apps/dcmftest dcmdata/apps/dcmgpdir dcmdata/apps/dcmodify dcmdata/apps/dump2dcm dcmdata/apps/img2dcm +dcmdata/apps/json2dcm dcmdata/apps/pdf2dcm dcmdata/apps/stl2dcm dcmdata/apps/xml2dcm diff --git a/ANNOUNCE b/ANNOUNCE index 646567c9..84f91b85 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -1,17 +1,17 @@ ANNOUNCEMENT -Version 3.6.9 of the OFFIS DCMTK (DICOM toolkit) software is now available for +Version 3.7.0 of the OFFIS DCMTK (DICOM ToolKit) software is now available for public release. This release includes the following main changes over the -previous version 3.6.8: +previous version 3.6.9: -- DCMTK 3.6.9 builds correctly on older and up-to-date versions of GNU gcc - (9.5.0 to 14.2.0), Clang (14.0.6 to 18.1.8), Apple Clang (14.0.3 to 15.0.0), +- DCMTK 3.7.0 builds correctly on older and up-to-date versions of GNU gcc + (10.5.0 to 14.2.0), Clang (14.0.6 to 18.1.8), Apple Clang (17.0.0), and Microsoft Visual Studio (2017 to 2022). - Tested with the following operating systems/environments: - Android on arm64 - FreeBSD on x86_64 - - Linux on x86_64 and x86 + - Linux on x86_64, x86 and s390x - MacOS X on x86_64 and arm64 - NetBSD on x86_64 - OpenBSD on x86_64 @@ -22,137 +22,102 @@ previous version 3.6.8: - Updated DICOM data dictionary, list of SOP classes, well-known frame of references, transfer syntaxes, code definitions, supported context group - classes, and directory record types for DICOM standard release 2024e: + classes, and directory record types for DICOM standard release 2025e. - - This also includes the latest attributes and SOP classes for the DICONDE - standard, e.g. for thermography images (based on ASTM E3440). +- Added initial support for the Deflated Image Frame Compression Transfer + Syntax, i.e. reading and writing of DICOM files and datasets that are + encoded with this new transfer syntax as well as basic network support. - - Also updated the DICOMDIR generation code and tools accordingly. +- Added support for the DICONDE Storage SOP Class for ultrasonic waveforms + introduced with DICOM 2025b (based on a revised version of ASTM E2663). +- Added support for creating, writing and reading Labelmap Segmentation Storage + objects to the "dcmseg" module. -- The new JPEG XL and HTJ2K transfer syntaxes as well as the encapsulated - uncompressed transfer syntax are now supported for reading and writing, i.e. - for both files and network transfer. However, encoders or decoders have not - been implemented yet. +- Added new command line tools "dcmencap" and "dcmdecap" that handle the + encapsulation and decapsulation of all currently defined Encapsulated + Document Storage SOP Classes in DICOM. The older tools "pdf2dcm", "cda2dcm", + "stl2dcm", "dcm2pdf" and "dcm2cda" are now deprecated and will be removed in + a future version of DCMTK. -- Added new command line tool dcm2img that unifies and replaces the tools - dcm2pnm, dcmj2pnm and dcml2pnm, and adds support for JPEG-LS as an export - format for image files. The command line options are identical to the older - tools, so that dcm2img can serve as a drop-in replacement: +- Added new command line tool "json2dcm" that converts the contents of a DICOM + dataset in JSON encoding to a binary DICOM file or dataset. The JSON + document is expected to conform to the "DICOM JSON Model" as defined in + DICOM Part 18, Section F. - - By default, the new command line tool determines the output format - automatically based on the extension of the output filename. +- The command line tool "dcm2json" can now create BulkDataURIs and related bulk + data files. - - The deprecated command line tools were replaced by stubs, which are provided - for the user's convenience, but will be removed with a future release. +- Improved the handling of JPEG image files as input to "img2dcm". Several + special cases such as different types of component subsampling, or lossy JPEG + images in RGB or CMYK color (instead of YCbCr) are now correctly handled. -- Added new command line tool dcm2cda that extracts a CDA document from a DICOM - Encapsulated CDA Storage SOP Instance and stores it in a separate file. +- The "storescp" tool can now limit the number of network associations handled + in parallel when operating in "fork mode", on all supported platforms. -- Replaced command line tool dcmgpdir by a stub that calls the more - comprehensive command line tool dcmmkdir. +- Enhanced Specific Character Set support of the command line tool "dcm2xml" + when converting DICOMDIR files. -- Further enhanced and updated DICOM Structured Reporting (SR) module "dcmsr": +- Added support for the optional Patient's Age attribute from the Patient + Study Module to the "dcmsr" module. This attribute is now written to and + read from a DICOM dataset as well as to/from the "dcmsr"-specific XML + document format (see "dsr2xml.xsd"). - - Added support for the new Waveform Annotation SR IOD (introduced with - Supplement 239). +- Enhanced support for TLS in command line tools "dcmqrscp" and "movescu". + Query/Retrieve can now be completely performed over TLS. - - Made URL prefix for hyperlinks to composite objects configurable. +- Added support for IPv6 in DCMTK's association acceptors. The support for + DICOM network connections over IPv6 is now considered complete. Both + association requestors and association acceptors can choose between IPv4 + only, IPv6 only, and dual-stack mode. The default behavior of the command + line tools is unchanged (i.e. IPv4 only.) - - Updated code definitions and supported context group classes (see above). +- Added APIs that allows the user to set the Implementation Class UID and + Implementation Version Name at runtime while writing a DICOM file, processing + an incoming network association request or configuring an outgoing network + association request. - - Fixed issue with various IOD constraint checkers (see CP-2084). +- Added "putAndInsertXXX()" helper methods for the still rather new 64-bit VRs + "SV", "UV" and "OV" to the "DcmItem" class. -- Added IPv6 support to DCMTK's association requestors. All DCMTK "client" - applications that only request outgoing DICOM network associations can now - explicitly select the protocol version to be used. IPv6 support is not yet - implemented for association acceptors ("server" applications). +- Improved handling of invalid DICOM images when processing with the + "DicomImage" class or the "dcm2img" command line tool. -- Various TLS enhancements: +- Improved performance of the "DcmList" class that is used internally for the + DICOM dataset parser. - - Added TLS support to the command line tools dcmqrscp and getscu. +- Further clean-up of configure tests that checked for features that are not + used anymore in DCMTK or that are not in use anymore on any of the supported + operating systems (e.g. pre-POSIX APIs and header files). - - Added support for the Modified BCP 195 RFC 8996 TLS Profile. - - - Added new command line option --list-profiles to all TLS-enabled tools. - This option prints a list of the TLS Secure Transport Connection Profiles - supported. - - - Removed support for OpenSSL 1.0.2 and 1.1.0 and added support for OpenSSL - 3.1.0 to 3.4.0. - -- Extended central DCMTK data structure where all SOP Classes are defined with - their associated properties, e.g. type and sub-type. - -- Largely enhanced basic transfer syntax class DcmXfer, e.g. to distinguish - more clearly between encapsulation and compression. Please note that some of - the old methods have been deprecated and will be removed in a future release. - -- Enhanced performance of OFGlobal class, especially when used in applications - with many threads that read global objects of this class concurrently. - -- New, fully standards compliant implementations of OFStandard::atof() and - OFStandard::ftoa(), DCMTK's locale independent conversion routines between - floating point numbers and text. - -- Removed support for ICU-based character set conversion. Since the oficonv - module in DCMTK supports all DICOM Specific Character Sets, the ICU support, - which was never complete, has been removed. - -- DCMTK now requires compilers to provide conformance to C++98 and supports - compilation with newer C++ versions up to C++20, which can be enabled via - CMake's CMAKE_CXX_STANDARD variable. By default, C++11 is now enabled on - compilers that support this. - -- CMake-related enhancements and other changes: - - - The configure process now respects CMake's CMAKE_CROSSCOMPILING_EMULATOR - variable. - - - Exposed the CMAKE_DEBUG_POSTFIX variable to the user. There are extra - options to also enable the postfix for Windows DLLs as well as executables. - -- Many configure tests related to outdated compilers or libraries were removed, - thus significantly speeding up the configuration process. - -- Fixed binary segmentations with certain dimensions (some cases where number - of total bits per frame is not divisible by 8) that were broken when being - serialized into a dataset. - -- Fixed various other issues that occurred after the official 3.6.8 release, +- Fixed various other issues that occurred after the official 3.6.9 release, and further improved the performance. See CHANGES file for details. Many people have contributed to this new release of DCMTK, appearing here in alphabetical order. Thank you very much for your support! - Christian Wetzel - David Gobbi - David Seifert - Giulio Simonetti - Helmut Steiner - Jean Pierre Bassenge + Ben Chen + Chuang Zhao + Ding zhengzheng + Drak + Emmanuel Tacheau of the Cisco Talos team Jean-Christophe Fillion-Robin - Jesper Alf Dam - Kevin Leonardic - Marcel Pham - Mario Galijot - Markus Sabin - Martin Zeiser of the Cisco Talos team + Jez Cooke + Kade Rashid + Khang Tran Mathieu Malaterre - Matt McCormick - Melanie Michels - Nils Bars - Peter Klotz - Phileas Lebada - Piotr Batko - Sam James + Matt Hancock + Oliver Klerx + Simeon Stoykov Sobhita Mercy - Yoshinaga Kosuke + Vasyl Horbatenko (GitHub user doskachok) + Zou Dikai - DCMTK forum users "andreasb", "Fabian Guenther", "nbeck", "Oleh", "saltcreek" + DCMTK forum users "cschreib", "hapap", "OliWe", "pierrechatelier", + "pintagliata", "saltcreek" and "Shaeto" - GitHub users "akaraivanov", "bananabr", "khangthk", "luissantosHCIT", - "malaterre", "mrbean-bremen", "percontation", "thewtex" + GitHub users "luk1337", "mrbean-bremen", "nbeck-SMT", "Oss-Auditor", + "parasite-lost", "reunanen" and "Vovasch" Members of the DCMTK Team who have worked on this release are: @@ -165,4 +130,4 @@ The DCMTK software can be downloaded via: https://dicom.offis.de/dcmtk or https://www.dcmtk.org/ -OFFIS e.V., Oldenburg, Germany, 2024-12-10 +OFFIS e.V., Oldenburg, Germany, 2025-12-15 diff --git a/CMake/3rdparty.cmake b/CMake/3rdparty.cmake index 510027c8..83790057 100644 --- a/CMake/3rdparty.cmake +++ b/CMake/3rdparty.cmake @@ -58,7 +58,7 @@ if(DCMTK_USE_FIND_PACKAGE) message(STATUS "Info: DCMTK PNG support will be enabled") set(WITH_LIBPNG 1) include_directories(${PNG_INCLUDE_DIR}) - set(LIBPNG_LIBS ${PNG_LIBRARY}) + set(LIBPNG_LIBS ${PNG_LIBRARY} ${LIBPNG_EXTRA_LIBS_STATIC}) endif() endif() @@ -249,8 +249,8 @@ else() set(LIBXML_INCDIR "${WITH_LIBXMLINC}/include") set(LIBXML_LIBDIR "${WITH_LIBXMLINC}/lib") # libxml2 2.13 and newer require bcrypt.lib on Windows. - set(LIBXML_LIBS bcrypt debug "${LIBXML_LIBDIR}/libxml2_d.lib" optimized "${LIBXML_LIBDIR}/libxml2_o.lib") - if (EXISTS "${LIBXML_LIBDIR}/iconv_o.lib") + set(LIBXML_LIBS bcrypt debug "${LIBXML_LIBDIR}/libxml2_d.lib" optimized "${LIBXML_LIBDIR}/libxml2_o.lib" ${LIBXML2_EXTRA_LIBS_STATIC}) + if(EXISTS "${LIBXML_LIBDIR}/iconv_o.lib") set(LIBXML_LIBS ${LIBXML_LIBS} debug "${LIBXML_LIBDIR}/iconv_d.lib" optimized "${LIBXML_LIBDIR}/iconv_o.lib") endif() message(STATUS "Info: DCMTK XML support will be enabled") @@ -269,7 +269,7 @@ else() if(WITH_LIBPNGINC) set(LIBPNG_INCDIR "${WITH_LIBPNGINC}/include") set(LIBPNG_LIBDIR "${WITH_LIBPNGINC}/lib") - set(LIBPNG_LIBS debug "${LIBPNG_LIBDIR}/libpng_d.lib" optimized "${LIBPNG_LIBDIR}/libpng_o.lib") + set(LIBPNG_LIBS debug "${LIBPNG_LIBDIR}/libpng_d.lib" optimized "${LIBPNG_LIBDIR}/libpng_o.lib" ${LIBPNG_EXTRA_LIBS_STATIC}) message(STATUS "Info: DCMTK PNG support will be enabled") set(WITH_LIBPNG 1) else() # turn off library if library path not set @@ -284,7 +284,7 @@ else() if(WITH_LIBTIFFINC) set(LIBTIFF_INCDIR "${WITH_LIBTIFFINC}/include") set(LIBTIFF_LIBDIR "${WITH_LIBTIFFINC}/lib") - set(LIBTIFF_LIBS debug "${LIBTIFF_LIBDIR}/libtiff_d.lib" optimized "${LIBTIFF_LIBDIR}/libtiff_o.lib") + set(LIBTIFF_LIBS debug "${LIBTIFF_LIBDIR}/libtiff_d.lib" optimized "${LIBTIFF_LIBDIR}/libtiff_o.lib" ${TIFF_EXTRA_LIBS_STATIC}) message(STATUS "Info: DCMTK TIFF support will be enabled") set(WITH_LIBTIFF 1) else() # turn off library if library path not set @@ -302,7 +302,7 @@ else() set(OPENSSL_INCDIR "${WITH_OPENSSLINC}/include") set(OPENSSL_LIBDIR "${WITH_OPENSSLINC}/lib") # starting with OpenSSL 1.1.0, the Windows crypt32 library is needed for a static link of OpenSSL. - set(OPENSSL_LIBS "crypt32" debug "${OPENSSL_LIBDIR}/dcmtkssl_d.lib" optimized "${OPENSSL_LIBDIR}/dcmtkssl_o.lib" debug "${OPENSSL_LIBDIR}/dcmtkcrypto_d.lib" optimized "${OPENSSL_LIBDIR}/dcmtkcrypto_o.lib") + set(OPENSSL_LIBS "crypt32" debug "${OPENSSL_LIBDIR}/dcmtkssl_d.lib" optimized "${OPENSSL_LIBDIR}/dcmtkssl_o.lib" debug "${OPENSSL_LIBDIR}/dcmtkcrypto_d.lib" optimized "${OPENSSL_LIBDIR}/dcmtkcrypto_o.lib" ${OPENSSL_EXTRA_LIBS_STATIC}) set(TEMP_INCLUDES "${CMAKE_REQUIRED_INCLUDES}") list(APPEND CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCDIR}") CHECK_CXX_SOURCE_COMPILES("extern \"C\" {\n#include \n}\nint main(){\n#if OPENSSL_VERSION_NUMBER < 0x10001000L\n#error OpenSSL too old\n#endif\n}\n" OPENSSL_VERSION_CHECK) @@ -342,7 +342,7 @@ else() if(WITH_SNDFILEINC) set(SNDFILE_INCDIR "${WITH_SNDFILEINC}/include") set(SNDFILE_LIBDIR "${WITH_SNDFILEINC}/lib") - set(SNDFILE_LIBS debug "${SNDFILE_LIBDIR}/libsndfile_d.lib" optimized "${SNDFILE_LIBDIR}/libsndfile_o.lib") + set(SNDFILE_LIBS debug "${SNDFILE_LIBDIR}/libsndfile_d.lib" optimized "${SNDFILE_LIBDIR}/libsndfile_o.lib" ${SNDFILE_EXTRA_LIBS_STATIC}) message(STATUS "Info: DCMTK SNDFILE support will be enabled") set(WITH_SNDFILE 1) else() # turn off library if library path not set @@ -373,14 +373,14 @@ else() # Unfortunately, OpenJPEG uses a version number in the include path. This needs special handling. file(GLOB OPENJPEG2_DIR "${WITH_OPENJPEGINC}/include/openjpeg*") find_path(WITH_OPENJPEGINC1 "openjpeg.h" "${OPENJPEG2_DIR}" NO_DEFAULT_PATH) - if ("${WITH_OPENJPEGINC1}" STREQUAL "WITH_OPENJPEGINC1-NOTFOUND") + if("${WITH_OPENJPEGINC1}" STREQUAL "WITH_OPENJPEGINC1-NOTFOUND") message(STATUS "Info: DCMTK OpenJPEG support will be disabled because the header files were not found.") set(DCMTK_WITH_OPENJPEG OFF CACHE BOOL "" FORCE) set(WITH_OPENJPEG "") else() set(OPENJPEG_INCDIR "${WITH_OPENJPEGINC1}") set(OPENJPEG_LIBDIR "${WITH_OPENJPEGINC}/lib") - set(OPENJPEG_LIBS debug "${OPENJPEG_LIBDIR}/openjp2_d.lib" optimized "${OPENJPEG_LIBDIR}/openjp2_o.lib") + set(OPENJPEG_LIBS debug "${OPENJPEG_LIBDIR}/openjp2_d.lib" optimized "${OPENJPEG_LIBDIR}/openjp2_o.lib" ${OPENJPEG_EXTRA_LIBS_STATIC}) message(STATUS "Info: DCMTK OpenJPEG support will be enabled") set(WITH_OPENJPEG 1) endif() diff --git a/CMake/CTest/CTestCustomAndroid.cmake.in b/CMake/CTest/CTestCustomAndroid.cmake.in index 087bb7aa..08ef1d62 100644 --- a/CMake/CTest/CTestCustomAndroid.cmake.in +++ b/CMake/CTest/CTestCustomAndroid.cmake.in @@ -5,7 +5,7 @@ # # Restore the required settings of the CMake configuration step -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.7.0...4.2.0 FATAL_ERROR) set(CMAKE_COMMAND "@CMAKE_COMMAND@") set(CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@") set(CMAKE_CURRENT_BINARY_DIR "@CMAKE_BINARY_DIR@") @@ -16,15 +16,18 @@ set(CMAKE_SHARED_LIBRARY_SUFFIX "@CMAKE_SHARED_LIBRARY_SUFFIX@") set(CMAKE_HOST_SYSTEM "@CMAKE_HOST_SYSTEM@") set(DCMTK_CMAKE_INCLUDE "@CMAKE_SOURCE_DIR@/@DCMTK_CMAKE_INCLUDE@") set(DCMTK_TEST_EXECUTABLES "@DCMTK_TEST_EXECUTABLES@") +set(DCMTK_TEST_EXECUTABLE_PATH "@DCMTK_TEST_EXECUTABLE_PATH@") set(DCMTK_ALL_LIBRARIES "@DCMTK_ALL_LIBRARIES@") set(DCMTK_LIBRARY_DEPENDENCIES "@DCMTK_LIBRARY_DEPENDENCIES@") set(DCMTK_PACKAGE_VERSION "@DCMTK_PACKAGE_VERSION@") set(DCMTK_ABI_VERSION "@DCMTK_ABI_VERSION@") set(BUILD_SHARED_LIBS "@BUILD_SHARED_LIBS@") set(DCMTK_DICOM_DICTIONARIES "@DCMTK_DICOM_DICTIONARIES@") +set(DCMTK_ICONV_DATAFILES "@DCMTK_ICONV_DATAFILES@") set(ANDROID "@ANDROID@") +set(ANDROID_EMULATOR_PORT "@ANDROID_EMULATOR_PORT@") set(ANDROID_ADB_PROGRAM "@ANDROID_ADB_PROGRAM@") -set(ANDROID_ANDROID_PROGRAM "@ANDROID_ANDROID_PROGRAM@") +set(ANDROID_AVDMANAGER_PROGRAM "@ANDROID_AVDMANAGER_PROGRAM@") set(ANDROID_EMULATOR_PROGRAM "@ANDROID_EMULATOR_PROGRAM@") set(ANDROID_EMULATOR_AVD "@ANDROID_EMULATOR_AVD@") set(ANDROID_RUNTIME_LIBRARIES "@ANDROID_RUNTIME_LIBRARIES@") @@ -71,13 +74,14 @@ DCMTK_ANDROID_PUSH(DCMTK_ANDROID_EMULATOR_INSTANCE ${ANDROID_RUNTIME_LIBRARIES} ${DCMTK_LIBRARY_DEPENDENCIES} ${DCMTK_CREATED_SHARED_LIBRARIES} - ${DCMTK_TEST_EXECUTABLES} + ${DCMTK_TEST_EXECUTABLE_PATH} ${DCMTK_DICOM_DICTIONARIES} + ${DCMTK_ICONV_DATAFILES} DESTINATION ${ANDROID_TEMPORARY_FILES_LOCATION} ) # Set executable permissions -foreach(TEST_EXECUTABLE ${DCMTK_TEST_EXECUTABLES}) +foreach(TEST_EXECUTABLE ${DCMTK_TEST_EXECUTABLE_PATH}) get_filename_component(NAME "${TEST_EXECUTABLE}" NAME) DCMTK_ANDROID_SHELL(DCMTK_ANDROID_EMULATOR_INSTANCE COMMAND chmod 755 "${ANDROID_TEMPORARY_FILES_LOCATION}/${NAME}" diff --git a/CMake/CTest/dcmtkCTestRunAndroid.cmake.in b/CMake/CTest/dcmtkCTestRunAndroid.cmake.in index c9d7663c..eda05075 100644 --- a/CMake/CTest/dcmtkCTestRunAndroid.cmake.in +++ b/CMake/CTest/dcmtkCTestRunAndroid.cmake.in @@ -22,7 +22,7 @@ set(DCMTK_ANDROID_EMULATOR_INSTANCE "$ENV{DCMTK_ANDROID_EMULATOR_INSTANCE}") # Run the actual testcase on the remote device DCMTK_ANDROID_SHELL(DCMTK_ANDROID_EMULATOR_INSTANCE - COMMAND "LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:${ANDROID_TEMPORARY_FILES_LOCATION}" "DCMDICTPATH=${DCMDICTPATH}" "${DCMTK_CTEST_TESTCASE_COMMAND}" $ENV{DCMTK_CTEST_EXTRA_ARGUMENTS} "${DCMTK_CTEST_TEST_NAME}" + COMMAND "LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:${ANDROID_TEMPORARY_FILES_LOCATION}" "DCMDICTPATH=${DCMDICTPATH}" "DCMICONVPATH=${DCMICONVPATH}" "${DCMTK_CTEST_TESTCASE_COMMAND}" $ENV{DCMTK_CTEST_EXTRA_ARGUMENTS} "${DCMTK_CTEST_TEST_NAME}" WORKING_DIRECTORY "${ANDROID_TEMPORARY_FILES_LOCATION}" RESULT_VARIABLE RESULT ) diff --git a/CMake/DCMTKConfig.cmake.in b/CMake/DCMTKConfig.cmake.in index 9ae5cfce..88f73c84 100644 --- a/CMake/DCMTKConfig.cmake.in +++ b/CMake/DCMTKConfig.cmake.in @@ -69,7 +69,6 @@ set(DCMTK_WIDE_CHAR_MAIN_FUNCTION @DCMTK_WIDE_CHAR_MAIN_FUNCTION@) set(DCMTK_ENABLE_LFS @DCMTK_ENABLE_LFS@) set(DCMTK_ENABLE_CHARSET_CONVERSION @DCMTK_ENABLE_CHARSET_CONVERSION@) - # CMake builtins set(DCMTK_CMAKE_BUILD_TYPE @CMAKE_BUILD_TYPE@) set(DCMTK_CMAKE_CXX_COMPILER "@CMAKE_CXX_COMPILER@") @@ -92,15 +91,17 @@ set(DCMTK_CMAKE_EXE_LINKER_FLAGS_RELEASE @CMAKE_EXE_LINKER_FLAGS_RELEASE@) set(DCMTK_CMAKE_EXE_LINKER_FLAGS_MINSIZEREL @CMAKE_EXE_LINKER_FLAGS_MINSIZEREL@) set(DCMTK_CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO @CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO@) -set(DCMTK_CMAKE_INSTALL_BINDIR @CMAKE_INSTALL_BINDIR@) -set(DCMTK_CMAKE_INSTALL_SYSCONFDIR @CMAKE_INSTALL_SYSCONFDIR@) -set(DCMTK_CMAKE_INSTALL_INCLUDEDIR @CMAKE_INSTALL_INCLUDEDIR@) -set(DCMTK_CMAKE_INSTALL_LIBDIR @CMAKE_INSTALL_LIBDIR@) -set(DCMTK_CMAKE_INSTALL_DATAROOTDIR @CMAKE_INSTALL_DATAROOTDIR@) +# DCMTK installation directories +set(DCMTK_CMAKE_INSTALL_BINDIR "@CMAKE_INSTALL_BINDIR@") +set(DCMTK_CMAKE_INSTALL_SYSCONFDIR "@CMAKE_INSTALL_SYSCONFDIR@") +set(DCMTK_CMAKE_INSTALL_INCLUDEDIR "@CMAKE_INSTALL_INCLUDEDIR@") +set(DCMTK_CMAKE_INSTALL_LIBDIR "@CMAKE_INSTALL_LIBDIR@") +set(DCMTK_CMAKE_INSTALL_DATAROOTDIR "@CMAKE_INSTALL_DATAROOTDIR@") +set(DCMTK_CMAKE_INSTALL_DATADIR "@CMAKE_INSTALL_DATADIR@") +set(DCMTK_CMAKE_INSTALL_DOCDIR "@CMAKE_INSTALL_DOCDIR@") set(DCMTK_CMAKE_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@") - SET_AND_CHECK(DCMTK_TARGETS "@PACKAGE_DCMTK_CMKDIR_CONFIG@/DCMTKTargets.cmake") @DCMTK_CONFIG_CODE@ diff --git a/CMake/GenerateDCMTKConfigure.cmake b/CMake/GenerateDCMTKConfigure.cmake index a3856d3d..ac3ae70a 100644 --- a/CMake/GenerateDCMTKConfigure.cmake +++ b/CMake/GenerateDCMTKConfigure.cmake @@ -126,7 +126,7 @@ DCMTK_UNSET(SYSTEM_PROCESSOR) # Define the complete package version name that will be used as a subdirectory # name for the installation of configuration files, data files and documents. -if (DCMTK_PACKAGE_VERSION_SUFFIX STREQUAL "+") +if(DCMTK_PACKAGE_VERSION_SUFFIX STREQUAL "+") # development version set(DCMTK_COMPLETE_PACKAGE_VERSION "${DCMTK_PACKAGE_VERSION}-${DCMTK_PACKAGE_DATE}") else() @@ -226,12 +226,9 @@ else() endif() # Check the sizes of various types -include (CheckTypeSize) -CHECK_TYPE_SIZE("double" SIZEOF_DOUBLE) -CHECK_TYPE_SIZE("float" SIZEOF_FLOAT) +include(CheckTypeSize) CHECK_TYPE_SIZE("int" SIZEOF_INT) CHECK_TYPE_SIZE("long" SIZEOF_LONG) -CHECK_TYPE_SIZE("short" SIZEOF_SHORT) CHECK_TYPE_SIZE("void*" SIZEOF_VOID_P) # Check for include files, libraries, and functions @@ -304,14 +301,12 @@ endif() CHECK_INCLUDE_FILE_CXX("cstdint" HAVE_CSTDINT) CHECK_INCLUDE_FILE_CXX("dirent.h" HAVE_DIRENT_H) CHECK_INCLUDE_FILE_CXX("err.h" HAVE_ERR_H) - CHECK_INCLUDE_FILE_CXX("fcntl.h" HAVE_FCNTL_H) CHECK_INCLUDE_FILE_CXX("fnmatch.h" HAVE_FNMATCH_H) CHECK_INCLUDE_FILE_CXX("grp.h" HAVE_GRP_H) CHECK_INCLUDE_FILE_CXX("ieeefp.h" HAVE_IEEEFP_H) CHECK_INCLUDE_FILE_CXX("io.h" HAVE_IO_H) CHECK_INCLUDE_FILE_CXX("langinfo.h" HAVE_LANGINFO_H) CHECK_INCLUDE_FILE_CXX("libc.h" HAVE_LIBC_H) - CHECK_INCLUDE_FILE_CXX("malloc.h" HAVE_MALLOC_H) CHECK_INCLUDE_FILE_CXX("mqueue.h" HAVE_MQUEUE_H) CHECK_INCLUDE_FILE_CXX("netdb.h" HAVE_NETDB_H) CHECK_INCLUDE_FILE_CXX("png.h" HAVE_PNG_H) @@ -319,7 +314,6 @@ endif() CHECK_INCLUDE_FILE_CXX("pthread.h" HAVE_PTHREAD_H) CHECK_INCLUDE_FILE_CXX("pwd.h" HAVE_PWD_H) CHECK_INCLUDE_FILE_CXX("semaphore.h" HAVE_SEMAPHORE_H) - CHECK_INCLUDE_FILE_CXX("stdint.h" HAVE_STDINT_H) CHECK_INCLUDE_FILE_CXX("strings.h" HAVE_STRINGS_H) CHECK_INCLUDE_FILE_CXX("synch.h" HAVE_SYNCH_H) CHECK_INCLUDE_FILE_CXX("sys/dir.h" HAVE_SYS_DIR_H) @@ -332,12 +326,10 @@ endif() CHECK_INCLUDE_FILE_CXX("sys/resource.h" HAVE_SYS_RESOURCE_H) CHECK_INCLUDE_FILE_CXX("sys/select.h" HAVE_SYS_SELECT_H) CHECK_INCLUDE_FILE_CXX("sys/socket.h" HAVE_SYS_SOCKET_H) - CHECK_INCLUDE_FILE_CXX("sys/stat.h" HAVE_SYS_STAT_H) CHECK_INCLUDE_FILE_CXX("sys/syscall.h" HAVE_SYS_SYSCALL_H) CHECK_INCLUDE_FILE_CXX("sys/systeminfo.h" HAVE_SYS_SYSTEMINFO_H) CHECK_INCLUDE_FILE_CXX("sys/time.h" HAVE_SYS_TIME_H) CHECK_INCLUDE_FILE_CXX("sys/timeb.h" HAVE_SYS_TIMEB_H) - CHECK_INCLUDE_FILE_CXX("sys/types.h" HAVE_SYS_TYPES_H) CHECK_INCLUDE_FILE_CXX("sys/un.h" HAVE_SYS_UN_H) CHECK_INCLUDE_FILE_CXX("sys/utime.h" HAVE_SYS_UTIME_H) CHECK_INCLUDE_FILE_CXX("sys/utsname.h" HAVE_SYS_UTSNAME_H) @@ -347,8 +339,6 @@ endif() CHECK_INCLUDE_FILE_CXX("unistd.h" HAVE_UNISTD_H) CHECK_INCLUDE_FILE_CXX("unix.h" HAVE_UNIX_H) CHECK_INCLUDE_FILE_CXX("utime.h" HAVE_UTIME_H) - CHECK_INCLUDE_FILE_CXX("system_error" HAVE_SYSTEM_ERROR) - CHECK_INCLUDE_FILE_CXX("tuple" HAVE_TUPLE) CHECK_INCLUDE_FILE_CXX("type_traits" HAVE_TYPE_TRAITS) CHECK_INCLUDE_FILE_CXX("atomic" HAVE_ATOMIC) @@ -363,11 +353,8 @@ endif() # This mimics the autoconf test. There are systems out there # (e.g. FreeBSD and NeXT) where tcp.h can't be compiled on its own. - set(TCP_H_DEPS "") - if(HAVE_SYS_TYPES_H) - # This one is needed to make FreeBSD happy - set(TCP_H_DEPS "sys/types.h") - endif() + # This one is needed to make FreeBSD happy + set(TCP_H_DEPS "sys/types.h") CHECK_INCLUDE_FILES("${TCP_H_DEPS};netinet/in_systm.h" HAVE_NETINET_IN_SYSTM_H) if(HAVE_NETINET_IN_SYSTM_H) set(TCP_H_DEPS "${TCP_H_DEPS};netinet/in_systm.h") @@ -393,9 +380,6 @@ endif() set(HAVE_NO_TYPEDEF_PID_T TRUE) else() set(HAVE_NO_TYPEDEF_PID_T FALSE) - if(NOT ${HAVE_SYS_TYPES_H}) - set(HAVE_NO_TYPEDEF_SSIZE_T TRUE) - endif() endif() set(HEADERS) @@ -431,11 +415,7 @@ endif() endif() set(HEADERS ${HEADERS} stdlib.h) - - if(HAVE_STDINT_H) - set(HEADERS ${HEADERS} stdint.h) - endif() - + set(HEADERS ${HEADERS} stdint.h) set(HEADERS ${HEADERS} stddef.h) if(HAVE_NETDB_H) @@ -464,17 +444,13 @@ endif() set(HEADERS ${HEADERS} sys/resource.h) endif() - if(HAVE_SYS_TYPES_H) - set(HEADERS ${HEADERS} sys/types.h) - endif() + set(HEADERS ${HEADERS} sys/types.h) if(HAVE_SYS_SOCKET_H) set(HEADERS ${HEADERS} sys/socket.h) endif() - if(HAVE_SYS_STAT_H) - set(HEADERS ${HEADERS} sys/stat.h) - endif() + set(HEADERS ${HEADERS} sys/stat.h) if(HAVE_SYS_TIMEB_H) set(HEADERS ${HEADERS} sys/timeb.h) @@ -514,9 +490,7 @@ endif() set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} iphlpapi ws2_32 netapi32 wsock32) endif() - if(HAVE_FENV_H) - set(HEADERS ${HEADERS} fenv.h) - endif() + set(HEADERS ${HEADERS} fenv.h) # std::vsnprintf and std::vsnprintf need the C++ version of the headers. # We just assume they exist when the C version was found @@ -541,12 +515,9 @@ endif() CHECK_FUNCTION_EXISTS(_findfirst HAVE__FINDFIRST) CHECK_FUNCTION_EXISTS(_set_output_format HAVE__SET_OUTPUT_FORMAT) - CHECK_FUNCTION_EXISTS(access HAVE_ACCESS) CHECK_FUNCTION_EXISTS(atoll HAVE_ATOLL) - CHECK_FUNCTION_EXISTS(bcmp HAVE_BCMP) CHECK_FUNCTION_EXISTS(cuserid HAVE_CUSERID) CHECK_FUNCTION_EXISTS(fgetln HAVE_FGETLN) - CHECK_FUNCTION_EXISTS(finite HAVE_FINITE) CHECK_FUNCTION_EXISTS(flock HAVE_FLOCK) CHECK_FUNCTION_EXISTS(fork HAVE_FORK) CHECK_FUNCTION_EXISTS(fseeko HAVE_FSEEKO) @@ -558,27 +529,18 @@ endif() CHECK_FUNCTION_EXISTS(gethostid HAVE_GETHOSTID) CHECK_FUNCTION_EXISTS(getlogin HAVE_GETLOGIN) CHECK_FUNCTION_EXISTS(getlogin_r HAVE_GETLOGIN_R) - CHECK_FUNCTION_EXISTS(getpid HAVE_GETPID) CHECK_FUNCTION_EXISTS(getpwnam HAVE_GETPWNAM) - CHECK_FUNCTION_EXISTS(getrusage HAVE_GETRUSAGE) CHECK_FUNCTION_EXISTS(gettimeofday HAVE_GETTIMEOFDAY) CHECK_FUNCTION_EXISTS(getuid HAVE_GETUID) CHECK_FUNCTION_EXISTS(gmtime_r HAVE_GMTIME_R) - CHECK_FUNCTION_EXISTS(index HAVE_INDEX) - CHECK_FUNCTION_EXISTS(itoa HAVE_ITOA) - CHECK_FUNCTION_EXISTS(listen HAVE_LISTEN) CHECK_FUNCTION_EXISTS(localtime_r HAVE_LOCALTIME_R) CHECK_FUNCTION_EXISTS(lockf HAVE_LOCKF) CHECK_FUNCTION_EXISTS(lstat HAVE_LSTAT) CHECK_FUNCTION_EXISTS(malloc_debug HAVE_MALLOC_DEBUG) CHECK_FUNCTION_EXISTS(mkstemp HAVE_MKSTEMP) - CHECK_FUNCTION_EXISTS(mktemp HAVE_MKTEMP) CHECK_FUNCTION_EXISTS(nanosleep HAVE_NANOSLEEP) - CHECK_FUNCTION_EXISTS(rindex HAVE_RINDEX) CHECK_FUNCTION_EXISTS(setuid HAVE_SETUID) CHECK_FUNCTION_EXISTS(sleep HAVE_SLEEP) - CHECK_FUNCTION_EXISTS(stat HAVE_STAT) - CHECK_FUNCTION_EXISTS(strdup HAVE_STRDUP) CHECK_FUNCTION_EXISTS(strlcat HAVE_STRLCAT) CHECK_FUNCTION_EXISTS(strlcpy HAVE_STRLCPY) CHECK_FUNCTION_EXISTS(sysinfo HAVE_SYSINFO) @@ -588,10 +550,6 @@ endif() CHECK_SYMBOL_EXISTS(strcasestr "string.h" HAVE_PROTOTYPE_STRCASESTR) - CHECK_FUNCTIONWITHHEADER_EXISTS(feenableexcept "${HEADERS}" HAVE_PROTOTYPE_FEENABLEEXCEPT) - CHECK_FUNCTIONWITHHEADER_EXISTS(finite "${HEADERS}" HAVE_PROTOTYPE_FINITE) - CHECK_FUNCTIONWITHHEADER_EXISTS("std::isinf(0.)" "${CXXHEADERS}" HAVE_PROTOTYPE_STD__ISINF) - CHECK_FUNCTIONWITHHEADER_EXISTS("std::isnan(0.)" "${CXXHEADERS}" HAVE_PROTOTYPE_STD__ISNAN) CHECK_FUNCTIONWITHHEADER_EXISTS(flock "${HEADERS}" HAVE_PROTOTYPE_FLOCK) CHECK_FUNCTIONWITHHEADER_EXISTS(gethostbyname_r "${HEADERS}" HAVE_PROTOTYPE_GETHOSTBYNAME_R) CHECK_FUNCTIONWITHHEADER_EXISTS(gethostbyaddr_r "${HEADERS}" HAVE_PROTOTYPE_GETHOSTBYADDR_R) @@ -602,14 +560,10 @@ endif() # not be defined in the standard C++ headers. CHECK_FUNCTIONWITHHEADER_EXISTS(_vsnprintf_s "${HEADERS}" HAVE__VSNPRINTF_S) CHECK_FUNCTIONWITHHEADER_EXISTS(vfprintf_s "${HEADERS}" HAVE_VFPRINTF_S) - CHECK_FUNCTIONWITHHEADER_EXISTS(vsnprintf "${HEADERS}" HAVE_VSNPRINTF) CHECK_FUNCTIONWITHHEADER_EXISTS(vsprintf_s "${HEADERS}" HAVE_VSPRINTF_S) - CHECK_FUNCTIONWITHHEADER_EXISTS(std::vfprintf "${CXXHEADERS}" HAVE_PROTOTYPE_STD__VFPRINTF) CHECK_FUNCTIONWITHHEADER_EXISTS(std::vsnprintf "${CXXHEADERS}" HAVE_PROTOTYPE_STD__VSNPRINTF) CHECK_FUNCTIONWITHHEADER_EXISTS(_stricmp "${HEADERS}" HAVE_PROTOTYPE__STRICMP) CHECK_FUNCTIONWITHHEADER_EXISTS(gettimeofday "${HEADERS}" HAVE_PROTOTYPE_GETTIMEOFDAY) - CHECK_FUNCTIONWITHHEADER_EXISTS(mkstemp "${HEADERS}" HAVE_PROTOTYPE_MKSTEMP) - CHECK_FUNCTIONWITHHEADER_EXISTS(mktemp "${HEADERS}" HAVE_PROTOTYPE_MKTEMP) CHECK_FUNCTIONWITHHEADER_EXISTS(strcasecmp "${HEADERS}" HAVE_PROTOTYPE_STRCASECMP) CHECK_FUNCTIONWITHHEADER_EXISTS(strncasecmp "${HEADERS}" HAVE_PROTOTYPE_STRNCASECMP) CHECK_FUNCTIONWITHHEADER_EXISTS(strerror_r "${HEADERS}" HAVE_PROTOTYPE_STRERROR_R) @@ -619,24 +573,16 @@ endif() CHECK_FUNCTIONWITHHEADER_EXISTS("__sync_sub_and_fetch((int*)0,0)" "${HEADERS}" HAVE_SYNC_SUB_AND_FETCH) CHECK_FUNCTIONWITHHEADER_EXISTS("InterlockedIncrement((long*)0)" "${HEADERS}" HAVE_INTERLOCKED_INCREMENT) CHECK_FUNCTIONWITHHEADER_EXISTS("InterlockedDecrement((long*)0)" "${HEADERS}" HAVE_INTERLOCKED_DECREMENT) - CHECK_FUNCTIONWITHHEADER_EXISTS("_fpclassf(0.0f)" "${HEADERS}" HAVE_PROTOTYPE__FPCLASSF) CHECK_FUNCTIONWITHHEADER_EXISTS("getgrnam_r((char*)0,(group*)0,(char*)0,0,(group**)0)" "${HEADERS}" HAVE_GETGRNAM_R) CHECK_FUNCTIONWITHHEADER_EXISTS("getpwnam_r((char*)0,(passwd*)0,(char*)0,0,(passwd**)0)" "${HEADERS}" HAVE_GETPWNAM_R) CHECK_FUNCTIONWITHHEADER_EXISTS("readdir_r((DIR*)0,(dirent*)0,(dirent**)0)" "${HEADERS}" HAVE_READDIR_R) CHECK_FUNCTIONWITHHEADER_EXISTS("readdir_r((DIR*)0,(dirent*)0)" "${HEADERS}" HAVE_OLD_READDIR_R) - CHECK_FUNCTIONWITHHEADER_EXISTS(nanosleep "${HEADERS}" HAVE_PROTOTYPE_NANOSLEEP) CHECK_FUNCTIONWITHHEADER_EXISTS("&passwd::pw_gecos" "${HEADERS}" HAVE_PASSWD_GECOS) CHECK_FUNCTIONWITHHEADER_EXISTS("TryAcquireSRWLockShared((PSRWLOCK)0)" "${HEADERS}" HAVE_PROTOTYPE_TRYACQUIRESRWLOCKSHARED) - # "definition" is an (exchangeable) identifier that is needed for successful compile test - CHECK_FUNCTIONWITHHEADER_EXISTS("fp_except_t definition" "${HEADERS}" HAVE_DECLARATION_FP_EXCEPT_T) # Check for some type definitions needed by JasPer and defines them if necessary # Even if not functions but types are looked for, the script works fine. # "definition" is an (exchangeable) identifier that is needed for successful compile test - CHECK_FUNCTIONWITHHEADER_EXISTS("uchar definition" "${HEADERS}" HAVE_UCHAR_TYPEDEF) - CHECK_FUNCTIONWITHHEADER_EXISTS("ushort definition" "${HEADERS}" HAVE_USHORT_TYPEDEF) - CHECK_FUNCTIONWITHHEADER_EXISTS("uint definition" "${HEADERS}" HAVE_UINT_TYPEDEF) - CHECK_FUNCTIONWITHHEADER_EXISTS("ulong definition" "${HEADERS}" HAVE_ULONG_TYPEDEF) CHECK_FUNCTIONWITHHEADER_EXISTS("long long definition" "${HEADERS}" HAVE_LONG_LONG) CHECK_FUNCTIONWITHHEADER_EXISTS("unsigned long long definition" "${HEADERS}" HAVE_UNSIGNED_LONG_LONG) CHECK_FUNCTIONWITHHEADER_EXISTS("int64_t definition" "${HEADERS}" HAVE_INT64_T) @@ -655,9 +601,6 @@ endif() CHECK_FUNCTIONWITHHEADER_EXISTS("popen" "${HEADERS}" HAVE_POPEN) CHECK_FUNCTIONWITHHEADER_EXISTS("pclose" "${HEADERS}" HAVE_PCLOSE) - # Signal handling functions - CHECK_FUNCTIONWITHHEADER_EXISTS("sigjmp_buf definition" "setjmp.h" HAVE_SIGJMP_BUF) - if(HAVE_LOCKF AND ANDROID) # When Android introduced lockf, they forgot to put the constants like F_LOCK in the # appropriate headers, this tests if they are defined and disables lockf if they are not @@ -704,7 +647,7 @@ else() #include -int main () +int main() { pthread_t p; unsigned long l = p; @@ -963,49 +906,6 @@ endfunction() DCMTK_CHECK_ENABLE_LFS() -if(WIN32) - # If someone can tell me how to convince TRY_COMPILE to link against winsock, - # we could use tests for these. Until then, here is what would be the result: - set(HAVE_INTP_ACCEPT 1 CACHE INTERNAL "Set if socket functions accept an int* argument") - set(HAVE_INTP_GETSOCKOPT 1 CACHE INTERNAL "Set if socket functions accept an int* argument") -else() - # Check if socket functions accept an int* - DCMTK_TRY_COMPILE(HAVE_INTP_SOCKET, "socket functions accept an int* argument" - " -#ifdef __cplusplus -extern \"C\" { -#endif -#ifdef _WIN32 -/* Windows is pure evil */ -#include -#else -#include -#endif -#ifdef __cplusplus -} -#endif - -int main() -{ - int i; - struct sockaddr *addr = 0; - int addrlen = 0; - int optlen = 0; - - i = accept(1, addr, &addrlen); - i = getsockopt(0, 0, 0, 0, &optlen); - - return 0; -}") - if(HAVE_INTP_SOCKET) - set(HAVE_INTP_ACCEPT 1 CACHE INTERNAL "Set if socket functions accept an int* argument") - set(HAVE_INTP_GETSOCKOPT 1 CACHE INTERNAL "Set if socket functions accept an int* argument") - else() - set(HAVE_INTP_ACCEPT 0 CACHE INTERNAL "Set if socket functions accept an int* argument") - set(HAVE_INTP_GETSOCKOPT 0 CACHE INTERNAL "Set if socket functions accept an int* argument") - endif() -endif() - # Check for alignment query / specifier support DCMTK_TRY_COMPILE(HAVE_GNU_ALIGNOF "__alignof__ is supported" "int main() @@ -1063,32 +963,6 @@ int main() return enable(0)) == sizeof(yes_type)>::result; }") -DCMTK_TRY_COMPILE(HAVE_STD_NAMESPACE "ANSI standard C++ includes use std namespace" - "#include -int main() -{ - using namespace std; - std::cout << endl; - return 0; -}") - -DCMTK_TRY_COMPILE(HAVE_STD__NOTHROW "the compiler supports std::nothrow" - "#include -int main() -{ - int* i = new (std::nothrow) int; - return 0; -}") - -DCMTK_TRY_COMPILE(HAVE_NOTHROW_DELETE "the compiler supports operator delete (std::nothrow)" - "#include -int main() -{ - int* i = 0; - operator delete (i,std::nothrow); - return 0; -}") - DCMTK_TRY_COMPILE(HAVE_STATIC_ASSERT "the compiler supports static_assert" "#include int main() @@ -1327,7 +1201,7 @@ endfunction() set(FORCE_MSVC_CPLUSPLUS_MACRO "") if(MSVC) if(NOT (MSVC_VERSION LESS 1910)) # VS 2017 and above - set (FORCE_MSVC_CPLUSPLUS_MACRO "/Zc:__cplusplus") + set(FORCE_MSVC_CPLUSPLUS_MACRO "/Zc:__cplusplus") endif() endif() @@ -1358,6 +1232,12 @@ if(MSVC) endforeach() endif() +if(NOT HAVE_CXX11 AND NOT DCMTK_PERMIT_CXX98) + # Since the situation where the user has explicitly requested CMAKE_CXX_STANDARD=98 + # has already been handled in dcmtkPrepare.cmake, we are apparently using a compiler + # that uses C++98 by default, and the user has not requested anything specific. + message(FATAL_ERROR "DCMTK will require C++11 or later in the future (which is apparently not supported by this compiler). Use cmake option -DDCMTK_PERMIT_CXX98=ON to override this error (for now).") +endif() if(CMAKE_CROSSCOMPILING) set(DCMTK_CROSS_COMPILING ${CMAKE_CROSSCOMPILING}) diff --git a/CMake/dcmtkMacros.cmake b/CMake/dcmtkMacros.cmake index bd354693..4ba68453 100644 --- a/CMake/dcmtkMacros.cmake +++ b/CMake/dcmtkMacros.cmake @@ -13,6 +13,9 @@ function(DCMTK_ADD_TESTS MODULE) string(REPLACE "\\" "\\\\" TEST_COMMAND "${TEST_COMMAND}") elseif(ANDROID) set(TEST_COMMAND "${ANDROID_TEMPORARY_FILES_LOCATION}/${MODULE}_tests") + set(MODULE_PATH "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${MODULE}_tests${CMAKE_EXECUTABLE_SUFFIX}") + list(APPEND DCMTK_TEST_EXECUTABLE_PATH "${MODULE_PATH}") + set(DCMTK_TEST_EXECUTABLE_PATH ${DCMTK_TEST_EXECUTABLE_PATH} CACHE INTERNAL "") else() # not reachable, handled by not defining DCMTK_RUN_CTEST_SCRIPT endif() diff --git a/CMake/dcmtkPrepare.cmake b/CMake/dcmtkPrepare.cmake index cfcfa443..973a6ea6 100644 --- a/CMake/dcmtkPrepare.cmake +++ b/CMake/dcmtkPrepare.cmake @@ -47,15 +47,15 @@ endif() # Basic version information set(DCMTK_MAJOR_VERSION 3) -set(DCMTK_MINOR_VERSION 6) -set(DCMTK_BUILD_VERSION 9) +set(DCMTK_MINOR_VERSION 7) +set(DCMTK_BUILD_VERSION 0) # The ABI is not guaranteed to be stable between different snapshots/releases, # so this particular version number is increased for each snapshot or release. -set(DCMTK_ABI_VERSION 19) +set(DCMTK_ABI_VERSION 20) # Package "release" settings (some are currently unused and, therefore, disabled) set(DCMTK_PACKAGE_NAME "dcmtk") -set(DCMTK_PACKAGE_DATE "2024-12-11") +set(DCMTK_PACKAGE_DATE "2025-12-15") set(DCMTK_PACKAGE_VERSION "${DCMTK_MAJOR_VERSION}.${DCMTK_MINOR_VERSION}.${DCMTK_BUILD_VERSION}") set(DCMTK_PACKAGE_VERSION_NUMBER ${DCMTK_MAJOR_VERSION}${DCMTK_MINOR_VERSION}${DCMTK_BUILD_VERSION}) set(DCMTK_PACKAGE_VERSION_SUFFIX "") @@ -67,7 +67,7 @@ set(DCMTK_PACKAGE_VERSION_SUFFIX "") option(DCMTK_LINK_STATIC "Statically link all libraries and tools with the runtime and third party libraries." OFF) # Modify linker flags and libraries for static builds if enabled by the user if(DCMTK_LINK_STATIC) - if (NOT APPLE) + if(NOT APPLE) # MacOS does not support static libraries. DCMTK_LINK_STATIC is still useful on # macOS though, since it will create binaries that only depend on macOS's libc. set(CMAKE_EXE_LINKER_FLAGS "-static") @@ -95,14 +95,14 @@ endif() option(DCMTK_PORTABLE_LINUX_BINARIES "Create ELF binaries while statically linking all third party libraries and libstdc++." OFF) if(DCMTK_PORTABLE_LINUX_BINARIES) - if (DCMTK_LINK_STATIC) + if(DCMTK_LINK_STATIC) message(FATAL_ERROR "Options DCMTK_LINK_STATIC and DCMTK_PORTABLE_LINUX_BINARIES are mutually exclusive.") endif() # only use static versions of third party libraries set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") # When using gcc and clang, use the static version of libgcc/libstdc++. - if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR + if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "ARMClang") OR @@ -128,10 +128,19 @@ endfunction() # This should possibly be enhanced by using find_package() at some point. The best solution # would probably be to compile all third-party libraries ourself. if(DCMTK_LINK_STATIC OR DCMTK_PORTABLE_LINUX_BINARIES) + get_static_library("STATIC_ZLIB" "libz.a") + set(LIBPNG_EXTRA_LIBS_STATIC "${STATIC_ZLIB}") get_static_library("STATIC_DL" "libdl.a") get_static_library("STATIC_LZMA" "liblzma.a") - get_static_library("STATIC_ZLIB" "libz.a") - set(LIBXML2_EXTRA_LIBS_STATIC "${STATIC_LZMA}" "${STATIC_ZLIB}" "${STATIC_DL}") + + # On Debian Linux, libxml2 depends on libicu. We do not want that dependency in our + # own builds since it massively increases the size of the executable binaries. + # If you still want to try, then enable the following two statements: + # + # get_static_library("STATIC_ICUUC" "libicuuc.a") + # get_static_library("STATIC_ICUDATA" "libicudata.a") + + set(LIBXML2_EXTRA_LIBS_STATIC "${STATIC_LZMA}" "${STATIC_ZLIB}" "${STATIC_DL}" "${STATIC_ICUUC}" "${STATIC_ICUDATA}") get_static_library("STATIC_PTHREAD" "libpthread.a") set(OPENJPEG_EXTRA_LIBS_STATIC "${STATIC_PTHREAD}") set(OPENSSL_EXTRA_LIBS_STATIC "${STATIC_DL}") @@ -146,10 +155,17 @@ if(DCMTK_LINK_STATIC OR DCMTK_PORTABLE_LINUX_BINARIES) get_static_library("STATIC_JBIG" "libjbig.a") get_static_library("STATIC_JPEG" "libjpeg.a") get_static_library("STATIC_DEFLATE" "libdeflate.a") - set(TIFF_EXTRA_LIBS_STATIC "${STATIC_WEBP}" "${STATIC_ZSTD}" "${STATIC_LZMA}" "${STATIC_JBIG}" "${STATIC_JBIG}" "${STATIC_DEFLATE}" "${STATIC_ZLIB}") + + # On Debian Linux, libtiff depends on libLerc. We do not need that dependency in our + # own builds. If want to use Debian's libtiff, then enable the following statement: + # + # get_static_library("STATIC_LIBLERC" "libLerc.a") + + set(TIFF_EXTRA_LIBS_STATIC "${STATIC_WEBP}" "${STATIC_ZSTD}" "${STATIC_LZMA}" "${STATIC_JBIG}" "${STATIC_JBIG}" "${STATIC_DEFLATE}" "${STATIC_ZLIB}" "${STATIC_LIBLERC}") get_static_library("STATIC_NSL" "libnsl.a") set(WRAP_EXTRA_LIBS_STATIC "${STATIC_NSL}") else() + set(LIBPNG_EXTRA_LIBS_STATIC) set(LIBXML2_EXTRA_LIBS_STATIC) set(OPENJPEG_EXTRA_LIBS_STATIC) set(OPENSSL_EXTRA_LIBS_STATIC) @@ -177,7 +193,7 @@ option(DCMTK_WITH_PNG "Configure DCMTK with support for PNG." ON) option(DCMTK_WITH_XML "Configure DCMTK with support for XML." ON) option(DCMTK_WITH_ZLIB "Configure DCMTK with support for ZLIB." ON) option(DCMTK_WITH_OPENSSL "Configure DCMTK with support for OPENSSL." ON) -option(DCMTK_WITH_SNDFILE "Configure DCMTK with support for SNDFILE." ON) +option(DCMTK_WITH_SNDFILE "Configure DCMTK with support for SNDFILE." OFF) option(DCMTK_WITH_ICONV "Configure DCMTK with support for ICONV." ON) if(NOT WIN32) option(DCMTK_WITH_WRAP "Configure DCMTK with support for WRAP." ON) @@ -224,7 +240,7 @@ else() endif() set(DCMTK_DEFAULT_DICT "${DCMTK_DEFAULT_DICT_DEFAULT}" CACHE STRING "Denotes whether DCMTK will use built-in (compiled-in), external (file), or no default dictionary on startup") set_property(CACHE DCMTK_DEFAULT_DICT PROPERTY STRINGS builtin external none) -if (DCMTK_DEFAULT_DICT EQUAL "none") +if(DCMTK_DEFAULT_DICT EQUAL "none") message(WARNING "Denotes whether DCMTK will use built-in (compiled-in), external (file), or no default dictionary on startup") endif() @@ -234,14 +250,14 @@ option(DCMTK_USE_DCMDICTPATH "Enable reading dictionary that is defined through # Declare the option DCMTK_ENABLE_BUILTIN_OFICONV_DATA, which by default is ON when # we are compiling shared libraries. -if (BUILD_SHARED_LIBS) +if(BUILD_SHARED_LIBS) option(DCMTK_ENABLE_BUILTIN_OFICONV_DATA "Embed oficonv data files into oficonv library" ON) else() option(DCMTK_ENABLE_BUILTIN_OFICONV_DATA "Embed oficonv data files into oficonv library" OFF) endif() # evaluate the option DCMTK_ENABLE_BUILTIN_OFICONV_DATA -if (DCMTK_ENABLE_BUILTIN_OFICONV_DATA) +if(DCMTK_ENABLE_BUILTIN_OFICONV_DATA) add_definitions(-DDCMTK_ENABLE_BUILTIN_OFICONV_DATA) endif() @@ -370,7 +386,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") if(WIN32) option(DCMTK_OVERWRITE_WIN32_COMPILER_FLAGS "Modify the default compiler flags selected by CMake." ON) option(DCMTK_COMPILE_WIN32_MULTITHREADED_DLL "Compile DCMTK using the Multithreaded DLL runtime library." OFF) - if (BUILD_SHARED_LIBS) + if(BUILD_SHARED_LIBS) set(DCMTK_COMPILE_WIN32_MULTITHREADED_DLL ON) endif() else() @@ -380,7 +396,7 @@ else() endif() if(WIN32 AND CMAKE_GENERATOR MATCHES "Visual Studio .*|NMake .*") - if (POLICY CMP0091) + if(POLICY CMP0091) # CMake 3.15 and newer use CMAKE_MSVC_RUNTIME_LIBRARY to select # the MSVC runtime library if(DCMTK_COMPILE_WIN32_MULTITHREADED_DLL OR BUILD_SHARED_LIBS) @@ -527,7 +543,7 @@ else() # ... for non-Windows systems endif() # When compiling with IBM xlC, add flags to suppress some noisy C++ warnings - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "XL") + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "XL") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -qsuppress=1500-029:1500-030") endif() @@ -551,28 +567,34 @@ endif() set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") +option(DCMTK_PERMIT_CXX98 "Permit (deprecated) compilation with C++98 language standard. This will cease to function in a future DCMTK release." OFF) + # If desired C++ standard is at least C++11, set DCMTK_MODERN_CXX_STANDARD to true # and remember it in global property DCMTK_MODERN_CXX_STANDARD. # This is later evaluated in GenerateDCMTKConfigure.cmake in order to check # whether the compiler actually supports the required C++ standards up to the # version specified in CMAKE_CXX_STANDARD. Finally, the highest C++ version # (<= CMAKE_CXX_STANDARD) will be selected that the compiler actually supports. -if (NOT DEFINED CMAKE_CXX_STANDARD) +if(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 11) set(DCMTK_MODERN_CXX_STANDARD TRUE) +elseif(CMAKE_CXX_STANDARD MATCHES "^9[0-9]?$" AND NOT DCMTK_PERMIT_CXX98) + message(FATAL_ERROR "DCMTK will require C++11 or later in the future. Use cmake option -DDCMTK_PERMIT_CXX98=ON to override this error (for now)") elseif(CMAKE_CXX_STANDARD MATCHES "^9[0-9]?$") set(DCMTK_MODERN_CXX_STANDARD FALSE) - message(WARNING "DCMTK will require C++11 or later in the future.") + message(WARNING "DCMTK will require C++11 or later in the future, continuing for now.") elseif(CMAKE_CXX_STANDARD GREATER 20) MESSAGE(WARNING "DCMTK is only known to compile for C++ versions <= 20 (C++${CMAKE_CXX_STANDARD} requested).") set(DCMTK_MODERN_CXX_STANDARD TRUE) else() # CMAKE_CXX_STANDARD is 11, 14, 17 or 20 set(DCMTK_MODERN_CXX_STANDARD TRUE) endif() + define_property(GLOBAL PROPERTY DCMTK_MODERN_CXX_STANDARD BRIEF_DOCS "TRUE when compiling C++11 (or newer) code." FULL_DOCS "TRUE when the compiler does support and is configured for C++11 or a later C++ standard." ) + # Remember globally that we use at least C++11 set_property(GLOBAL PROPERTY DCMTK_MODERN_CXX_STANDARD ${DCMTK_MODERN_CXX_STANDARD}) # Build global list of all modern C++ standard versions supported so far @@ -597,12 +619,12 @@ if(MSVC) add_compile_options("/W4") else() # Add -Wall to the compiler flags if we are compiling with gcc or Clang. - if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR - (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR - (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") OR - (CMAKE_CXX_COMPILER_ID STREQUAL "ARMClang") OR - (CMAKE_CXX_COMPILER_ID STREQUAL "XLClang")) - add_compile_options("-Wall") + if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR + (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR + (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") OR + (CMAKE_CXX_COMPILER_ID STREQUAL "ARMClang") OR + (CMAKE_CXX_COMPILER_ID STREQUAL "XLClang")) + add_compile_options("-Wall") endif() endif() @@ -720,8 +742,10 @@ function(DCMTK_TEST_SOCKET_LIBRARY NAME SYMBOL) endif() endfunction() -DCMTK_TEST_SOCKET_LIBRARY(nsl "gethostbyname") -DCMTK_TEST_SOCKET_LIBRARY(socket "socket") +if(CMAKE_SYSTEM_NAME MATCHES "SunOS" AND CMAKE_SYSTEM_VERSION VERSION_LESS "11.4") + DCMTK_TEST_SOCKET_LIBRARY(nsl "gethostbyname") + DCMTK_TEST_SOCKET_LIBRARY(socket "socket") +endif() #----------------------------------------------------------------------------- # Test if SunPro compiler and add features diff --git a/CMake/dcmtkUseAndroidSDK.cmake b/CMake/dcmtkUseAndroidSDK.cmake index 17232d68..6a1cc92e 100644 --- a/CMake/dcmtkUseAndroidSDK.cmake +++ b/CMake/dcmtkUseAndroidSDK.cmake @@ -22,15 +22,9 @@ # * STOPPED - the emulated device has been # shutdown or is currently being # shutdown -# EMULATOR_UUID: a generated ID to identify the emulator -# instance. Meant to prevent accessing the wrong device -# in case multiple Android devices are accessible. # EMULATOR_NAME: the name of the emulator instance. # all running emulator instances are named by the SDK -# in a locally unique fashion. The name will only be -# available in case the state is 'RUNNING', since -# only the name of running devices can be retrieved -# by UUID matching. +# in a locally unique fashion from the provided port. # An emulator instance is accessed by the different # tools of the NDK (e. g. 'adb') by referring to the # instance with the value of EMULATOR_NAME. @@ -42,23 +36,19 @@ include(CMakeParseArguments) # individual components. # VAR - the emulator instance handle object to unpack # EMULATOR_STATE - will contain the emulators state -# EMULATOR_UUID - will contain the generated UUID that -# identifies the emulator instance # EMULATOR_NAME - will contain the instance name, required # to access the emulator via 'adb' etc. -# All three outputs will be unset if the object in VAR +# All two outputs will be unset if the object in VAR # is not a valid emulator instance handle. # All additional arguments will be ignored. # macro(DCMTK_ANDROID_GET_OBJECT_PROPERTIES VAR) list(LENGTH ${VAR} ${VAR}_LENGTH) - if(${VAR}_LENGTH EQUAL 3) + if(${VAR}_LENGTH EQUAL 2) list(GET ${VAR} 0 EMULATOR_STATE) - list(GET ${VAR} 1 EMULATOR_UUID) - list(GET ${VAR} 2 EMULATOR_NAME) + list(GET ${VAR} 1 EMULATOR_NAME) else() unset(EMULATOR_STATE) - unset(EMULATOR_UUID) unset(EMULATOR_NAME) endif() endmacro() @@ -69,12 +59,11 @@ endmacro() # VAR - the name of the variable that shall contain the # resulting instance handle object. # EMULATOR_STATE - the state to set -# EMULATOR_UUID - the UUID to set # EMULATOR_NAME - the name to set # All additional arguments will be ignored. # -macro(DCMTK_ANDROID_SET_OBJECT_PROPERTIES VAR EMULATOR_STATE EMULATOR_UUID EMULATOR_NAME) - set(${VAR} "${EMULATOR_STATE}" "${EMULATOR_UUID}" "${EMULATOR_NAME}" CACHE INTERNAL "") +macro(DCMTK_ANDROID_SET_OBJECT_PROPERTIES VAR EMULATOR_STATE EMULATOR_NAME) + set(${VAR} "${EMULATOR_STATE}" "${EMULATOR_NAME}" CACHE INTERNAL "") endmacro() # @@ -117,7 +106,7 @@ endfunction() # function(DCMTK_SETUP_ANDROID_EMULATOR) if(NOT ANDROID_TEMPORARY_FILES_LOCATION) - set(ANDROID_TEMPORARY_FILES_LOCATION "/cache" CACHE STRING "The path on the Android device that should be used for temporary files") + set(ANDROID_TEMPORARY_FILES_LOCATION "/data/local/cache" CACHE STRING "The path on the Android device that should be used for temporary files") endif() if(NOT ANDROID_SDK_ROOT) if(CMAKE_HOST_SYSTEM MATCHES "Windows.*") @@ -127,20 +116,20 @@ function(DCMTK_SETUP_ANDROID_EMULATOR) set(ANDROID_SDK_ROOT "/opt/android-sdk" CACHE PATH "Location of the Android SDK") endif() endif() - find_program(ANDROID_EMULATOR_PROGRAM emulator PATHS ${ANDROID_SDK_ROOT} PATH_SUFFIXES tools NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + find_program(ANDROID_EMULATOR_PROGRAM emulator PATHS ${ANDROID_SDK_ROOT} PATH_SUFFIXES emulator NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) if(CMAKE_HOST_SYSTEM MATCHES "Windows.*") - find_program(ANDROID_ANDROID_PROGRAM android.bat PATHS ${ANDROID_SDK_ROOT} PATH_SUFFIXES tools NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + find_program(ANDROID_AVDMANAGER_PROGRAM avdmanager PATHS ${ANDROID_SDK_ROOT} PATH_SUFFIXES tools NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) else() - find_program(ANDROID_ANDROID_PROGRAM android PATHS ${ANDROID_SDK_ROOT} PATH_SUFFIXES tools NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + find_program(ANDROID_AVDMANAGER_PROGRAM avdmanager PATHS ${ANDROID_SDK_ROOT} PATH_SUFFIXES cmdline-tools/latest/bin NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) endif() find_program(ANDROID_ADB_PROGRAM adb PATHS ${ANDROID_SDK_ROOT} PATH_SUFFIXES platform-tools NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) - if(NOT ANDROID_EMULATOR_PROGRAM OR NOT ANDROID_ANDROID_PROGRAM OR NOT ANDROID_ADB_PROGRAM) + if(NOT ANDROID_EMULATOR_PROGRAM OR NOT ANDROID_AVDMANAGER_PROGRAM OR NOT ANDROID_ADB_PROGRAM) message(FATAL_ERROR "Failed to detect the Android SDK, please set ANDROID_SDK_ROOT to the location of your Android SDK" "or set the missing tools manually!" ) else() - execute_process(COMMAND "${ANDROID_ANDROID_PROGRAM}" list avd RESULT_VARIABLE RESULT OUTPUT_VARIABLE OUTPUT ERROR_QUIET) + execute_process(COMMAND "${ANDROID_AVDMANAGER_PROGRAM}" list avd RESULT_VARIABLE RESULT OUTPUT_VARIABLE OUTPUT ERROR_QUIET) string(REGEX MATCHALL "Name:[ \t]*[^\r\n]*" ANDROID_AVAILABLE_AVDS ${OUTPUT}) string(REGEX REPLACE "Name:[ \t]*([^\r\n;]*)" "\\1" ANDROID_AVAILABLE_AVDS "${ANDROID_AVAILABLE_AVDS}") set(ANDROID_EMULATOR_AVD "${ANDROID_EMULATOR_AVD}" CACHE STRING "Android emulator Android Virtual Device (AVD) configuration" FORCE) @@ -190,60 +179,52 @@ function(DCMTK_ANDROID_LIST_EMULATORS ONLINE OFFLINE) endfunction() # -# Generate a random ID that is hopefully unique -# enough to be used as the instance UUID. +# Generate the emulator name from the provided port. +# Since the port decides the name of the emulator +# anyway, this is just to check if the emulator really +# exists under the expected name. # VAR - the name of the variable that will receive -# the generated UUID as a string value +# the generated name as a string value # Will ignore all additional arguments. # -function(DCMTK_ANDROID_EMULATOR_GENERATE_UUID VAR) - string(RANDOM LENGTH 20 RAND) - string(TIMESTAMP TM) - set(${VAR} "${TM}${RAND}") - string(MD5 ${VAR} ${${VAR}}) +function(DCMTK_ANDROID_EMULATOR_GENERATE_NAME_FROM_PORT VAR) + set(${VAR} "emulator-${ANDROID_EMULATOR_PORT}") set(${VAR} ${${VAR}} PARENT_SCOPE) endfunction() # -# Tries to query the UUID property of an accessible Android device. +# Tries to query the emulator name of an accessible Android device. # EMULATOR_NAME - the device name, as returned by DCMTK_ANDROID_LIST_EMULATORS -# VAR - the name of the variable that will be set to the device UUID +# VAR - the name of the variable that will be set to the emulator name # Will unset the variable referred to by VAR if no device with the given name # is accessible or the device is offline. # Will ignore all additional arguments. # -function(DCMTK_ANDROID_GET_EMULATOR_UUID EMULATOR_NAME VAR) - execute_process( - COMMAND "${ANDROID_ADB_PROGRAM}" -s "${EMULATOR_NAME}" shell getprop "ro.emu.uuid" - RESULT_VARIABLE RESULT - OUTPUT_VARIABLE OUTPUT - ERROR_QUIET - ) +function(DCMTK_ANDROID_GET_EMULATOR_NAME_FROM_PORT EMULATOR_NAME VAR) DCMTK_UNSET_PARENT_SCOPE(${VAR}) - if(NOT RESULT) - string(STRIP "${OUTPUT}" UUID) - if(UUID) - set("${VAR}" ${UUID} PARENT_SCOPE) - endif() + if(ANDROID_EMULATOR_PORT) + set(EMULATOR_NAME "emulator-${ANDROID_EMULATOR_PORT}" PARENT_SCOPE) endif() endfunction() # # Retrieves the name of the emulator instance referred to by -# the given UUID. Will wait until the device becomes online +# the given port. Will wait until the device becomes online # to query the name. # VAR - the name of the variable that will be set # to the device name -# EMULATOR_UUID - the emulator UUID of the device to inquired +# EMULATOR_NAME_FROM_PORT - the emulator name of the device to inquired # Will wait until all available devices are online or the right # one has been found. # Will ignore all additional arguments. # -function(DCMTK_ANDROID_GET_EMULATOR_NAME VAR EMULATOR_UUID) +function(DCMTK_ANDROID_GET_EMULATOR_NAME VAR EMULATOR_NAME_FROM_PORT) DCMTK_ANDROID_LIST_EMULATORS(ONLINE_EMULATORS OFFLINE_EMULATORS) foreach(EMULATOR ${ONLINE_EMULATORS}) - DCMTK_ANDROID_GET_EMULATOR_UUID("${EMULATOR}" UUID) - if(EMULATOR_UUID STREQUAL UUID) + DCMTK_ANDROID_GET_EMULATOR_NAME_FROM_PORT("${EMULATOR}" ANDROID_EMULATOR_PORT) + # The emulator could have a different name in another language, but the port should always + # be the same. + if(EMULATOR_NAME MATCHES ".*-${ANDROID_EMULATOR_PORT}") set("${VAR}" "${EMULATOR}" PARENT_SCOPE) return() endif() @@ -259,9 +240,8 @@ function(DCMTK_ANDROID_GET_EMULATOR_NAME VAR EMULATOR_UUID) ERROR_QUIET ) if(NOT RESULT) - DCMTK_ANDROID_GET_EMULATOR_UUID("${EMULATOR}" UUID) - if(UUID) - if(EMULATOR_UUID STREQUAL UUID) + if(ANDROID_EMULATOR_PORT) + if(EMULATOR_NAME STREQUAL "emulator-${ANDROID_EMULATOR_PORT}") set("${VAR}" "${EMULATOR}" PARENT_SCOPE) return() endif() @@ -287,36 +267,41 @@ endfunction() # function(DCMTK_ANDROID_START_EMULATOR VAR) DCMTK_SETUP_ANDROID_EMULATOR() + if(NOT ANDROID_EMULATOR_PORT) + message(FATAL_ERROR "Please provide the port the Android emulator should use. The range is 5554 to 5682 and the port should be an even number.") + else() + set(ANDROID_EMULATOR_PORT ${ANDROID_EMULATOR_PORT} CACHE INTERNAL "") + endif() if(NOT ANDROID_EMULATOR_AVD) message(FATAL_ERROR "Please select which Android emulator Android Virtual Device (AVD) configuration to use!") else() DCMTK_ANDROID_GET_OBJECT_PROPERTIES("${VAR}") if(NOT EMULATOR_STATE) - DCMTK_ANDROID_EMULATOR_GENERATE_UUID(EMULATOR_UUID) + DCMTK_ANDROID_EMULATOR_GENERATE_NAME_FROM_PORT(EMULATOR_NAME_FROM_PORT) elseif(EMULATOR_STATE STREQUAL "RUNNING") - DCMTK_ANDROID_GET_EMULATOR_UUID("${EMULATOR_NAME}" UUID) + DCMTK_ANDROID_GET_EMULATOR_NAME_FROM_PORT("${EMULATOR_NAME}" EMULATOR_NAME_FROM_PORT) # Do nothing if the running emulator instance is ok and can be reused. # Otherwise restart it. - if(UUID STREQUAL EMULATOR_UUID) + if(EMULATOR_NAME_FROM_PORT STREQUAL EMULATOR_NAME) message(STATUS "Reusing already running Android device emulator...") return() endif() elseif(EMULATOR_STATE STREQUAL "STARTING") # Is it really starting, or has somebody aborted it? message(STATUS "Found previously started Android device emulator, checking if it's still present...") - DCMTK_ANDROID_GET_EMULATOR_NAME(EMULATOR_NAME "${EMULATOR_UUID}") + DCMTK_ANDROID_GET_EMULATOR_NAME(EMULATOR_NAME "${EMULATOR_NAME_FROM_PORT}") if(EMULATOR_NAME) message(STATUS "Found previously started Android device emulator, checking if it's still present... yes") - DCMTK_ANDROID_SET_OBJECT_PROPERTIES(${VAR} RUNNING "${EMULATOR_UUID}" "${EMULATOR_NAME}") + DCMTK_ANDROID_SET_OBJECT_PROPERTIES(${VAR} RUNNING "${EMULATOR_NAME}") return() endif() message(STATUS "Found previously started Android device emulator, checking if it's still present... no") endif() message(STATUS "Starting the Android device emulator...") if(CMAKE_HOST_SYSTEM MATCHES "Windows.*") - set(COMMAND sh -c "${ANDROID_EMULATOR_PROGRAM} -avd ${ANDROID_EMULATOR_AVD} -no-boot-anim -prop ro.emu.uuid=${EMULATOR_UUID} >${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android-emulator.log 2>&1 < /dev/null &") + set(COMMAND sh -c "${ANDROID_EMULATOR_PROGRAM} -avd ${ANDROID_EMULATOR_AVD} -no-boot-anim -port ${ANDROID_EMULATOR_PORT} >${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android-emulator.log 2>&1 < /dev/null &") else() - set(COMMAND sh -c "${ANDROID_EMULATOR_PROGRAM} -avd ${ANDROID_EMULATOR_AVD} -no-window -no-boot-anim -prop ro.emu.uuid=${EMULATOR_UUID} >${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android-emulator.log 2>&1 < /dev/null &") + set(COMMAND sh -c "${ANDROID_EMULATOR_PROGRAM} -avd ${ANDROID_EMULATOR_AVD} -no-window -no-boot-anim -port ${ANDROID_EMULATOR_PORT} >${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android-emulator.log 2>&1 < /dev/null &") endif() execute_process( COMMAND ${COMMAND} @@ -325,7 +310,7 @@ function(DCMTK_ANDROID_START_EMULATOR VAR) ERROR_QUIET ) if(NOT RESULT) - DCMTK_ANDROID_SET_OBJECT_PROPERTIES("${VAR}" STARTING "${EMULATOR_UUID}" "") + DCMTK_ANDROID_SET_OBJECT_PROPERTIES("${VAR}" STARTING "${EMULATOR_NAME}") else() DCMTK_ANDROID_DESTROY_OBJECT("${VAR}") message(FATAL_ERROR "Error starting Android emulator.") @@ -337,7 +322,7 @@ endfunction() # Restart adb/the emulated device in root mode so that we gain write access to # the device. # Newer versions of the SDK seem to require this for doing anything meaningful -# with it. +# with it. It also only works on emulated devices without Playstore API. # EMULATOR_NAME - the name of the emulated device that shall be rooted. # Will ignore all additional arguments. # @@ -347,9 +332,8 @@ function(DCMTK_ANDROID_ADB_ROOT EMULATOR_NAME) OUTPUT_QUIET ERROR_QUIET ) - # the SDK was seemingly designed by a five year old, the device will - # become invisible while it is being rooted, therefore, wait until - # it is ready again + # The device will become invisible while it is being rooted, therefore, + # wait until it is ready again. set(STATUS 1) while(STATUS) execute_process( @@ -383,10 +367,10 @@ function(DCMTK_ANDROID_WAIT_FOR_EMULATOR VAR) else() message(STATUS "Waiting until the Android device emulator is ready to receive instructions...") while(NOT EMULATOR_NAME) - DCMTK_ANDROID_GET_EMULATOR_NAME(EMULATOR_NAME "${EMULATOR_UUID}") + DCMTK_ANDROID_GET_EMULATOR_NAME(EMULATOR_NAME "${EMULATOR_NAME_FROM_PORT}") endwhile() DCMTK_ANDROID_ADB_ROOT("${EMULATOR_NAME}") - DCMTK_ANDROID_SET_OBJECT_PROPERTIES("${VAR}" RUNNING "${EMULATOR_UUID}" "${EMULATOR_NAME}") + DCMTK_ANDROID_SET_OBJECT_PROPERTIES("${VAR}" RUNNING "${EMULATOR_NAME}") endif() endfunction() @@ -453,7 +437,7 @@ function(DCMTK_ANDROID_STOP_EMULATOR VAR) ERROR_QUIET ) if(NOT RESULT) - DCMTK_ANDROID_SET_OBJECT_PROPERTIES("${VAR}" STOPPED "${EMULATOR_UUID}" "") + DCMTK_ANDROID_SET_OBJECT_PROPERTIES("${VAR}" STOPPED "${EMULATOR_NAME}") else() message(WARNING "Unable to stop the android device emulator, please shutdown \"${EMULATOR_NAME}\" manually!") endif() diff --git a/CMake/osconfig.h.in b/CMake/osconfig.h.in index a84aee0c..6d39b4e4 100644 --- a/CMake/osconfig.h.in +++ b/CMake/osconfig.h.in @@ -62,17 +62,14 @@ /* Define the environment variable path separator */ #define ENVIRONMENT_PATH_SEPARATOR '@ENVIRONMENT_PATH_SEPARATOR@' -/* Define to 1 if you have the `access' function. */ -#cmakedefine HAVE_ACCESS @HAVE_ACCESS@ - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ALLOCA_H @HAVE_ALLOCA_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ARPA_INET_H @HAVE_ARPA_INET_H@ -/* Define to 1 if you have the `bcmp' function. */ -#cmakedefine HAVE_BCMP @HAVE_BCMP@ +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_CSTDINT @HAVE_CSTDINT@ /* Define to 1 if you have the `cuserid' function. */ #cmakedefine HAVE_CUSERID @HAVE_CUSERID@ @@ -80,9 +77,6 @@ /* Define to 1 if you have the `fgetln' function. */ #cmakedefine HAVE_FGETLN @HAVE_FGETLN@ -/* Define if your system has a declaration for fp_except_t in ieeefp.h */ -#cmakedefine HAVE_DECLARATION_FP_EXCEPT_T @HAVE_DECLARATION_FP_EXCEPT_T@ - /* Define to 1 if you have the header file, and it defines `DIR'.*/ #cmakedefine HAVE_DIRENT_H @HAVE_DIRENT_H@ @@ -92,12 +86,6 @@ /* Define to 1 if defined ENAMETOOLONG. */ #cmakedefine HAVE_ENAMETOOLONG @HAVE_ENAMETOOLONG@ -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_FCNTL_H @HAVE_FCNTL_H@ - -/* Define to 1 if you have the `finite' function. */ -#cmakedefine HAVE_FINITE @HAVE_FINITE@ - /* Define to 1 if you have the `flock' function. */ #cmakedefine HAVE_FLOCK @HAVE_FLOCK@ @@ -140,15 +128,9 @@ /* Define to 1 if you have the `getlogin_r' function. */ #cmakedefine HAVE_GETLOGIN_R @HAVE_GETLOGIN_R@ -/* Define to 1 if you have the `getpid' function. */ -#cmakedefine HAVE_GETPID @HAVE_GETPID@ - /* Define to 1 if you have the `getpwnam' function. */ #cmakedefine HAVE_GETPWNAM @HAVE_GETPWNAM@ -/* Define to 1 if you have the `getrusage' function. */ -#cmakedefine HAVE_GETRUSAGE @HAVE_GETRUSAGE@ - /* Define to 1 if you have the `gettimeofday' function. */ #cmakedefine HAVE_GETTIMEOFDAY @HAVE_GETTIMEOFDAY@ @@ -164,41 +146,18 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_IEEEFP_H @HAVE_IEEEFP_H@ -/* Define to 1 if you have the `index' function. */ -#cmakedefine HAVE_INDEX @HAVE_INDEX@ - -/* Define if your system declares argument 3 of accept() as int * instead of - size_t * or socklen_t * */ -#cmakedefine HAVE_INTP_ACCEPT @HAVE_INTP_ACCEPT@ - -/* Define if your system declares argument 5 of getsockopt() as int * instead - of size_t * or socklen_t */ -#cmakedefine HAVE_INTP_GETSOCKOPT @HAVE_INTP_GETSOCKOPT@ - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_IO_H @HAVE_IO_H@ -/* Define to 1 if you have the `itoa' function. */ -#cmakedefine HAVE_ITOA @HAVE_ITOA@ - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LANGINFO_H @HAVE_LANGINFO_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LIBC_H @HAVE_LIBC_H@ -/* Define to 1 if you have the `nsl' library (-lnsl). */ -#cmakedefine HAVE_LIBNSL @HAVE_LIBNSL@ - /* Define to 1 if the header shall be used instead of . */ #cmakedefine HAVE_LIBPNG_PNG_H @HAVE_LIBPNG_PNG_H@ -/* Define to 1 if you have the `socket' library (-lsocket). */ -#cmakedefine HAVE_LIBSOCKET @HAVE_LIBSOCKET@ - -/* Define to 1 if you have the `listen' function. */ -#cmakedefine HAVE_LISTEN @HAVE_LISTEN@ - /* Define to 1 if you have the `localtime_r' function. */ #cmakedefine HAVE_LOCALTIME_R @HAVE_LOCALTIME_R@ @@ -211,15 +170,9 @@ /* Define to 1 if you have the `malloc_debug' function. */ #cmakedefine HAVE_MALLOC_DEBUG @HAVE_MALLOC_DEBUG@ -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_MALLOC_H @HAVE_MALLOC_H@ - /* Define to 1 if you have the `mkstemp' function. */ #cmakedefine HAVE_MKSTEMP @HAVE_MKSTEMP@ -/* Define to 1 if you have the `mktemp' function. */ -#cmakedefine HAVE_MKTEMP @HAVE_MKTEMP@ - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MQUEUE_H @HAVE_MQUEUE_H@ @@ -241,12 +194,6 @@ /* Define to 1 if you have readdir_r */ #cmakedefine HAVE_READDIR_R @HAVE_READDIR_R@ -/* Define if your system has a prototype for feenableexcept in fenv.h */ -#cmakedefine HAVE_PROTOTYPE_FEENABLEEXCEPT @HAVE_PROTOTYPE_FEENABLEEXCEPT@ - -/* Define if your system has a prototype for finite in math.h */ -#cmakedefine HAVE_PROTOTYPE_FINITE @HAVE_PROTOTYPE_FINITE@ - /* Define to 1 if your has a prototype for `TryAcquireSRWLockShared' in windows.h (Win32 only). */ #cmakedefine HAVE_PROTOTYPE_TRYACQUIRESRWLOCKSHARED @HAVE_PROTOTYPE_TRYACQUIRESRWLOCKSHARED@ @@ -269,26 +216,6 @@ unistd.h */ #cmakedefine HAVE_PROTOTYPE_GETTIMEOFDAY @HAVE_PROTOTYPE_GETTIMEOFDAY@ -/* Define if your system has a prototype for std::isinf in cmath */ -#cmakedefine HAVE_PROTOTYPE_STD__ISINF @HAVE_PROTOTYPE_STD__ISINF@ - -/* Define if your system has a prototype for std::isnan in cmath */ -#cmakedefine HAVE_PROTOTYPE_STD__ISNAN @HAVE_PROTOTYPE_STD__ISNAN@ - -/* Define if your system has a prototype for fpclassf in math.h */ -#cmakedefine HAVE_PROTOTYPE__FPCLASSF @HAVE_PROTOTYPE__FPCLASSF@ - -/* Define if your system has a prototype for mkstemp in libc.h unistd.h - stdlib.h */ -#cmakedefine HAVE_PROTOTYPE_MKSTEMP @HAVE_PROTOTYPE_MKSTEMP@ - -/* Define if your system has a prototype for mktemp in libc.h unistd.h - stdlib.h */ -#cmakedefine HAVE_PROTOTYPE_MKTEMP @HAVE_PROTOTYPE_MKTEMP@ - -/* Define if your system has a prototype for std::vfprintf in stdarg.h */ -#cmakedefine HAVE_PROTOTYPE_STD__VFPRINTF @HAVE_PROTOTYPE_STD__VFPRINTF@ - /* Define if your system has a prototype for std::vsnprintf in stdio.h */ #cmakedefine HAVE_PROTOTYPE_STD__VSNPRINTF @HAVE_PROTOTYPE_STD__VSNPRINTF@ @@ -319,9 +246,6 @@ /* Define if your system has a prototype for _stricmp in string.h */ #cmakedefine HAVE_PROTOTYPE__STRICMP @HAVE_PROTOTYPE__STRICMP@ -/* Define if your system has a prototype for nanosleep in time.h */ -#cmakedefine HAVE_PROTOTYPE_NANOSLEEP @HAVE_PROTOTYPE_NANOSLEEP@ - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PTHREAD_H @HAVE_PTHREAD_H@ @@ -331,9 +255,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PWD_H @HAVE_PWD_H@ -/* Define to 1 if you have the `rindex' function. */ -#cmakedefine HAVE_RINDEX @HAVE_RINDEX@ - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SEMAPHORE_H @HAVE_SEMAPHORE_H@ @@ -343,21 +264,6 @@ /* Define to 1 if you have the `sleep' function. */ #cmakedefine HAVE_SLEEP @HAVE_SLEEP@ -/* Define to 1 if you have the `stat' function. */ -#cmakedefine HAVE_STAT @HAVE_STAT@ - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_STDINT_H @HAVE_STDINT_H@ - -/* Define to 1 if you have the C++11 header file. */ -#cmakedefine HAVE_SYSTEM_ERROR @HAVE_SYSTEM_ERROR@ - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_CSTDINT @HAVE_CSTDINT@ - -/* Define to 1 if you have the `strdup' function. */ -#cmakedefine HAVE_STRDUP @HAVE_STRDUP@ - /* Define to 1 if `strerror_r' returns a char*. */ #cmakedefine HAVE_CHARP_STRERROR_R @HAVE_CHARP_STRERROR_R@ @@ -397,9 +303,6 @@ /* Define to 1 if you have the header file, and it defines `DIR'.*/ #cmakedefine HAVE_SYS_DIR_H @HAVE_SYS_DIR_H@ -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_SYS_ERRNO_H @HAVE_SYS_ERRNO_H@ - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_FILE_H @HAVE_SYS_FILE_H@ @@ -424,9 +327,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SOCKET_H @HAVE_SYS_SOCKET_H@ -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_SYS_STAT_H @HAVE_SYS_STAT_H@ - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SYSCALL_H @HAVE_SYS_SYSCALL_H@ @@ -436,9 +336,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TIME_H @HAVE_SYS_TIME_H@ -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_SYS_TYPES_H @HAVE_SYS_TYPES_H@ - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_UN_H @HAVE_SYS_UN_H@ @@ -454,9 +351,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_THREAD_H @HAVE_THREAD_H@ -/* Define to 1 if you have the C++11 header file. */ -#cmakedefine HAVE_TUPLE @HAVE_TUPLE@ - /* Define to 1 if you have the C++11 header file. */ #cmakedefine HAVE_TYPE_TRAITS @HAVE_TYPE_TRAITS@ @@ -481,9 +375,6 @@ /* Define to 1 if you have the `vfprintf_s' function. */ #cmakedefine HAVE_VFPRINTF_S @HAVE_VFPRINTF_S@ -/* Define to 1 if you have the `vsnprintf' function. */ -#cmakedefine HAVE_VSNPRINTF @HAVE_VSNPRINTF@ - /* Define to 1 if you have the `vsprintf_s' function. */ #cmakedefine HAVE_VSPRINTF_S @HAVE_VSPRINTF_S@ @@ -551,21 +442,12 @@ /* Select LFS mode (defined above) that shall be used or don't define it */ #cmakedefine DCMTK_ENABLE_LFS @DCMTK_LFS_MODE@ -/* The size of a `double', as computed by sizeof. */ -#cmakedefine SIZEOF_DOUBLE @SIZEOF_DOUBLE@ - -/* The size of a `float', as computed by sizeof. */ -#cmakedefine SIZEOF_FLOAT @SIZEOF_FLOAT@ - /* The size of a `int', as computed by sizeof. */ #cmakedefine SIZEOF_INT @SIZEOF_INT@ /* The size of a `long', as computed by sizeof. */ #cmakedefine SIZEOF_LONG @SIZEOF_LONG@ -/* The size of a `short', as computed by sizeof. */ -#cmakedefine SIZEOF_SHORT @SIZEOF_SHORT@ - /* The size of a `void *', as computed by sizeof. */ #cmakedefine SIZEOF_VOID_P @SIZEOF_VOID_P@ @@ -654,27 +536,6 @@ #endif #endif -/* Set typedefs as needed for JasPer library */ -#cmakedefine HAVE_UCHAR_TYPEDEF -#ifndef HAVE_UCHAR_TYPEDEF -typedef unsigned char uchar; -#endif - -#cmakedefine HAVE_USHORT_TYPEDEF -#ifndef HAVE_USHORT_TYPEDEF -typedef unsigned short ushort; -#endif - -#cmakedefine HAVE_UINT_TYPEDEF -#ifndef HAVE_UINT_TYPEDEF -typedef unsigned int uint; -#endif - -#cmakedefine HAVE_ULONG_TYPEDEF -#ifndef HAVE_ULONG_TYPEDEF -typedef unsigned long ulong; -#endif - #cmakedefine HAVE_LONG_LONG #cmakedefine HAVE_UNSIGNED_LONG_LONG @@ -682,63 +543,39 @@ typedef unsigned long ulong; #cmakedefine HAVE_UINT64_T @HAVE_UINT64_T@ /* Additional settings for Borland C++ Builder */ -#ifdef __BORLANDC__ + +#if defined(__BORLANDC__) && (!defined(_WIN64) || !defined(__MINGW64__)) +#define HAVE_CLASSIC_BORLAND_COMPILER +#endif + +#ifdef HAVE_CLASSIC_BORLAND_COMPILER +#define _MSC_VER 1200 /* Treat Borland C++ 5.5 as MSVC6. */ #define _stricmp stricmp /* _stricmp in MSVC is stricmp in Borland C++ */ #define _strnicmp strnicmp /* _strnicmp in MSVC is strnicmp in Borland C++ */ - #pragma warn -8027 /* disable Warning W8027 "functions containing while are not expanded inline" */ - #pragma warn -8004 /* disable Warning W8004 "variable is assigned a value that is never used" */ - #pragma warn -8012 /* disable Warning W8012 "comparing signed and unsigned values" */ +#pragma warn -8027 /* disable Warning W8027 "functions containing while are not expanded inline" */ +#pragma warn -8004 /* disable Warning W8004 "variable is assigned a value that is never used" */ +#pragma warn -8012 /* disable Warning W8012 "comparing signed and unsigned values" */ #ifdef WITH_THREADS #define __MT__ /* required for _beginthreadex() API in */ #define _MT /* required for _errno on BCB6 */ #endif -#define HAVE_PROTOTYPE_MKTEMP 1 -#undef HAVE_SYS_UTIME_H -#define _MSC_VER 1200 /* Treat Borland C++ 5.5 as MSVC6. */ -#endif /* __BORLANDC__ */ - -/* Platform specific settings for Visual C++ - * By default, enable ANSI standard C++ includes on Visual C++ 6 and newer - * _MSC_VER == 1100 on Microsoft Visual C++ 5.0 - * _MSC_VER == 1200 on Microsoft Visual C++ 6.0 - * _MSC_VER == 1300 on Microsoft Visual C++ 7.0 - * _MSC_VER == 1310 on Microsoft Visual C++ 7.1 - * _MSC_VER == 1400 on Microsoft Visual C++ 8.0 - */ +#endif /* HAVE_CLASSIC_BORLAND_COMPILER */ -#ifdef _MSC_VER -#if _MSC_VER <= 1200 /* Additional settings for VC6 and older */ -/* disable warning that return type for 'identifier::operator ->' is not a UDT or reference to a UDT */ - #pragma warning( disable : 4284 ) -#define HAVE_OLD_INTERLOCKEDCOMPAREEXCHANGE 1 -#else -#define HAVE_VSNPRINTF 1 -#endif /* _MSC_VER <= 1200 */ +/* Platform specific settings for Visual C++ */ +#ifdef _MSC_VER #pragma warning( disable : 4251 ) /* disable warnings about needed dll-interface */ /* http://www.unknownroad.com/rtfm/VisualStudio/warningC4251.html */ #pragma warning( disable : 4099 ) /* disable warning about mismatched class and struct keywords */ /* http://alfps.wordpress.com/2010/06/22/cppx-is-c4099-really-a-sillywarning-disabling-msvc-sillywarnings */ #pragma warning( disable : 4521 ) /* disable warnings about multiple copy constructors and assignment operators,*/ #pragma warning( disable : 4522 ) /* since these are sometimes necessary for correct overload resolution*/ - -#if _MSC_VER >= 1400 /* Additional settings for Visual Studio 2005 and newer */ #pragma warning( disable : 4996 ) /* disable warnings about "deprecated" C runtime functions */ #if _MSC_VER < 1900 /* Warning only available before Visual Studio 2015 */ #pragma warning( disable : 4351 ) /* disable warnings about "new behavior" when initializing the elements of an array */ #endif /* _MSC_VER < 1900 */ -#endif /* _MSC_VER >= 1400 */ #endif /* _MSC_VER */ -/* Define if ANSI standard C++ includes use std namespace */ -#cmakedefine HAVE_STD_NAMESPACE @HAVE_STD_NAMESPACE@ - -/* Define if the compiler supports std::nothrow */ -#cmakedefine HAVE_STD__NOTHROW @HAVE_STD__NOTHROW@ - -/* Define if the compiler supports operator delete (std::nothrow) */ -#cmakedefine HAVE_NOTHROW_DELETE @HAVE_NOTHROW_DELETE@ - /* Define if the compiler supports static_assert */ #cmakedefine HAVE_STATIC_ASSERT @HAVE_STATIC_ASSERT@ @@ -760,9 +597,6 @@ typedef unsigned long ulong; /* Define if the compiler supports __declspec(deprecated("message")) */ #cmakedefine HAVE_DECLSPEC_DEPRECATED_MSG @HAVE_DECLSPEC_DEPRECATED_MSG@ -/* Define if your system has a prototype for std::vfprintf in stdarg.h */ -#cmakedefine HAVE_PROTOTYPE_STD__VFPRINTF @HAVE_PROTOTYPE_STD__VFPRINTF@ - /* Define if your system has off64_t */ #cmakedefine HAVE_OFF64_T @HAVE_OFF64_T@ @@ -775,39 +609,73 @@ typedef unsigned long ulong; /* Define if your system uses _pclose instead of pclose */ #cmakedefine HAVE_PCLOSE @HAVE_PCLOSE@ -/* Define if your system provides sigjmp_buf as conditional jmp_buf alternative */ -#cmakedefine HAVE_SIGJMP_BUF @HAVE_SIGJMP_BUF@ - /* Define if we can use C++11 */ #cmakedefine HAVE_CXX11 @HAVE_CXX11@ -#if defined(HAVE_CXX11) && defined(__cplusplus) && __cplusplus < 201103L +#if defined(HAVE_CXX11) +# if defined(_MSVC_LANG) +# if _MSVC_LANG < 201103L +#error \ +DCMTK was configured to use C++11 features, but your compiler does not or was not configured to provide them. +# endif +# elif defined(__cplusplus) +# if __cplusplus < 201103L #error \ DCMTK was configured to use C++11 features, but your compiler does not or was not configured to provide them. +# endif +# endif #endif /* Define if we can use C++14 */ #cmakedefine HAVE_CXX14 @HAVE_CXX14@ -#if defined(HAVE_CXX14) && defined(__cplusplus) && __cplusplus < 201402L +#if defined(HAVE_CXX14) +# if defined(_MSVC_LANG) +# if _MSVC_LANG < 201402L #error \ DCMTK was configured to use C++14 features, but your compiler does not or was not configured to provide them. +# endif +# elif defined(__cplusplus) +# if __cplusplus < 201402L +#error \ +DCMTK was configured to use C++14 features, but your compiler does not or was not configured to provide them. +# endif +# endif #endif /* Define if we can use C++17 */ #cmakedefine HAVE_CXX17 @HAVE_CXX17@ -#if defined(HAVE_CXX17) && defined(__cplusplus) && __cplusplus < 201703L +#if defined(HAVE_CXX17) +# if defined(_MSVC_LANG) +# if _MSVC_LANG < 201703L #error \ DCMTK was configured to use C++17 features, but your compiler does not or was not configured to provide them. +# endif +# elif defined(__cplusplus) +# if __cplusplus < 201703L +#error \ +DCMTK was configured to use C++17 features, but your compiler does not or was not configured to provide them. +# endif +# endif #endif + /* Define if we can use C++20 */ #cmakedefine HAVE_CXX20 @HAVE_CXX20@ -#if defined(HAVE_CXX20) && defined(__cplusplus) && __cplusplus < 202002L +#if defined(HAVE_CXX20) +# if defined(_MSVC_LANG) +# if _MSVC_LANG < 202002L #error \ DCMTK was configured to use C++20 features, but your compiler does not or was not configured to provide them. +# endif +# elif defined(__cplusplus) +# if __cplusplus < 202002L +#error \ +DCMTK was configured to use C++20 features, but your compiler does not or was not configured to provide them. +# endif +# endif #endif /* Define if the compiler supports __alignof__ */ @@ -867,9 +735,6 @@ DCMTK was configured to use C++20 features, but your compiler does not or was no /* Define if we are supposed to use STL's atomic */ #cmakedefine HAVE_STL_ATOMIC @HAVE_STL_ATOMIC@ -/* Define if the input iterator category is supported */ -#cmakedefine HAVE_CONTIGUOUS_ITERATOR_CATEGORY @HAVE_CONTIGUOUS_ITERATOR_CATEGORY@ - /* Feature Tests for the OpenSSL Library */ /* Define if your OpenSSL library provides the RAND_egd function */ @@ -911,13 +776,13 @@ DCMTK was configured to use C++20 features, but your compiler does not or was no /* Define if we have the header file*/ #cmakedefine HAVE_OPENSSL_PROVIDER_H @HAVE_OPENSSL_PROVIDER_H@ - /* Historical C/C++ language features and APIs that are guaranteed by C++98, - * C89, or POSIX.1-2001 for Posix platforms, and thus not tested anymore. + * C99, or POSIX.1-2001 for Posix platforms, or are not used anywhere in DCMTK, + * and thus not tested anymore. * These macros are deprecated and will be removed from a future version. */ - #if defined(POISON_DEPRECATED_FEATURE_MACROS) && defined(__GNUC__) +#pragma GCC poison HAVE_ACCESS #pragma GCC poison HAVE_ASSERT_H #pragma GCC poison HAVE_BIDIRECTIONAL_ITERATOR_CATEGORY #pragma GCC poison HAVE_CLASS_TEMPLATE @@ -934,12 +799,14 @@ DCMTK was configured to use C++20 features, but your compiler does not or was no #pragma GCC poison HAVE_DYNAMIC_CAST #pragma GCC poison HAVE_ERRNO_H #pragma GCC poison HAVE_EXPLICIT_TEMPLATE_SPECIALIZATION +#pragma GCC poison HAVE_FCNTL_H #pragma GCC poison HAVE_FENV_H #pragma GCC poison HAVE_FLOAT_H #pragma GCC poison HAVE_FORWARD_ITERATOR_CATEGORY #pragma GCC poison HAVE_FSTREAM #pragma GCC poison HAVE_FUNCTION_TEMPLATE #pragma GCC poison HAVE_GETENV +#pragma GCC poison HAVE_GETPID #pragma GCC poison HAVE_INPUT_ITERATOR_CATEGORY #pragma GCC poison HAVE_INTTYPES_H #pragma GCC poison HAVE_IOMANIP @@ -957,40 +824,59 @@ DCMTK was configured to use C++20 features, but your compiler does not or was no #pragma GCC poison HAVE_MEMCPY #pragma GCC poison HAVE_MEMMOVE #pragma GCC poison HAVE_MEMSET +#pragma GCC poison HAVE_MKTEMP +#pragma GCC poison HAVE_NOTHROW_DELETE #pragma GCC poison HAVE_OUTPUT_ITERATOR_CATEGORY #pragma GCC poison HAVE_PROTOTYPE_ISINF #pragma GCC poison HAVE_PROTOTYPE_ISNAN +#pragma GCC poison HAVE_PROTOTYPE_MKTEMP +#pragma GCC poison HAVE_PROTOTYPE_STD__ISINF +#pragma GCC poison HAVE_PROTOTYPE_STD__ISNAN +#pragma GCC poison HAVE_PROTOTYPE_STD__VFPRINTF +#pragma GCC poison HAVE_PROTOTYPE_VSNPRINTF #pragma GCC poison HAVE_RANDOM_ACCESS_ITERATOR_CATEGORY #pragma GCC poison HAVE_REINTERPRET_CAST #pragma GCC poison HAVE_SETJMP_H #pragma GCC poison HAVE_SIGNAL_H #pragma GCC poison HAVE_SSTREAM +#pragma GCC poison HAVE_STAT #pragma GCC poison HAVE_STATIC_CAST #pragma GCC poison HAVE_STATIC_TEMPLATE_METHOD #pragma GCC poison HAVE_STDARG_H #pragma GCC poison HAVE_STDBOOL_H #pragma GCC poison HAVE_STDDEF_H +#pragma GCC poison HAVE_STDINT_H #pragma GCC poison HAVE_STDIO_H #pragma GCC poison HAVE_STDLIB_H +#pragma GCC poison HAVE_STD_NAMESPACE +#pragma GCC poison HAVE_STD__NOTHROW #pragma GCC poison HAVE_STRCHR +#pragma GCC poison HAVE_STRDUP #pragma GCC poison HAVE_STRERROR #pragma GCC poison HAVE_STRING_H #pragma GCC poison HAVE_STRSTR #pragma GCC poison HAVE_STRSTREAM #pragma GCC poison HAVE_STRTOUL +#pragma GCC poison HAVE_SYS_STAT_H +/* #pragma GCC poison HAVE_SYS_TYPES_H */ /* this macro is used in some Linux system header files. */ #pragma GCC poison HAVE_TEMPNAM #pragma GCC poison HAVE_TIME_H #pragma GCC poison HAVE_TMPNAM #pragma GCC poison HAVE_TYPENAME #pragma GCC poison HAVE_VPRINTF +#pragma GCC poison HAVE_VSNPRINTF #pragma GCC poison HAVE_WCHAR_H #pragma GCC poison HAVE_WCSTOMBS #pragma GCC poison HAVE_WCTYPE_H #pragma GCC poison SIZEOF_CHAR +#pragma GCC poison SIZEOF_DOUBLE +#pragma GCC poison SIZEOF_FLOAT +#pragma GCC poison SIZEOF_SHORT #pragma GCC poison STDC_HEADERS #endif #if !defined(POISON_DEPRECATED_FEATURE_MACROS) || !defined(__GNUC__) +#define HAVE_ACCESS 1 #define HAVE_ASSERT_H 1 #define HAVE_BIDIRECTIONAL_ITERATOR_CATEGORY 1 #define HAVE_CLASS_TEMPLATE 1 @@ -1007,12 +893,14 @@ DCMTK was configured to use C++20 features, but your compiler does not or was no #define HAVE_DYNAMIC_CAST 1 #define HAVE_ERRNO_H 1 #define HAVE_EXPLICIT_TEMPLATE_SPECIALIZATION 1 +#define HAVE_FCNTL_H 1 #define HAVE_FENV_H 1 #define HAVE_FLOAT_H 1 #define HAVE_FORWARD_ITERATOR_CATEGORY 1 #define HAVE_FSTREAM 1 #define HAVE_FUNCTION_TEMPLATE 1 #define HAVE_GETENV 1 +#define HAVE_GETPID 1 #define HAVE_INPUT_ITERATOR_CATEGORY 1 #define HAVE_INTTYPES_H 1 #define HAVE_IOMANIP 1 @@ -1030,32 +918,47 @@ DCMTK was configured to use C++20 features, but your compiler does not or was no #define HAVE_MEMCPY 1 #define HAVE_MEMMOVE 1 #define HAVE_MEMSET 1 +#define HAVE_MKTEMP 1 +#define HAVE_NOTHROW_DELETE 1 #define HAVE_OUTPUT_ITERATOR_CATEGORY 1 #define HAVE_PROTOTYPE_ISINF 1 #define HAVE_PROTOTYPE_ISNAN 1 +#define HAVE_PROTOTYPE_MKTEMP 1 +#define HAVE_PROTOTYPE_STD__ISINF 1 +#define HAVE_PROTOTYPE_STD__ISNAN 1 +#define HAVE_PROTOTYPE_STD__VFPRINTF 1 +#define HAVE_PROTOTYPE_VSNPRINTF 1 #define HAVE_RANDOM_ACCESS_ITERATOR_CATEGORY 1 #define HAVE_REINTERPRET_CAST 1 #define HAVE_SETJMP_H 1 #define HAVE_SIGNAL_H 1 #define HAVE_SSTREAM 1 +#define HAVE_STAT 1 #define HAVE_STATIC_CAST 1 #define HAVE_STATIC_TEMPLATE_METHOD 1 #define HAVE_STDARG_H 1 #define HAVE_STDBOOL_H 1 #define HAVE_STDDEF_H 1 +#define HAVE_STDINT_H 1 #define HAVE_STDIO_H 1 #define HAVE_STDLIB_H 1 +#define HAVE_STD_NAMESPACE 1 +#define HAVE_STD__NOTHROW 1 #define HAVE_STRCHR 1 +#define HAVE_STRDUP 1 #define HAVE_STRERROR 1 #define HAVE_STRING_H 1 #define HAVE_STRSTR 1 #define HAVE_STRSTREAM 1 #define HAVE_STRTOUL 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TYPES_H 1 #define HAVE_TEMPNAM 1 #define HAVE_TIME_H 1 #define HAVE_TMPNAM 1 #define HAVE_TYPENAME 1 #define HAVE_VPRINTF 1 +#define HAVE_VSNPRINTF 1 #define HAVE_WCHAR_H 1 #define HAVE_WCSTOMBS 1 #define HAVE_WCTYPE_H 1 @@ -1069,29 +972,56 @@ DCMTK was configured to use C++20 features, but your compiler does not or was no #if defined(POISON_DEPRECATED_FEATURE_MACROS) && defined(__GNUC__) #pragma GCC poison C_INLINE +#pragma GCC poison HAVE_BCMP #pragma GCC poison HAVE_BCOPY +#pragma GCC poison HAVE_CONTIGUOUS_ITERATOR_CATEGORY +#pragma GCC poison HAVE_DECLARATION_FP_EXCEPT_T #pragma GCC poison HAVE_DECLARATION_STD__IOS_BASE__OPENMODE #pragma GCC poison HAVE_DOPRNT #pragma GCC poison HAVE_EMPTY_ARGC_ARGV +#pragma GCC poison HAVE_FINITE #pragma GCC poison HAVE_FSTREAM_H +#pragma GCC poison HAVE_GETRUSAGE +#pragma GCC poison HAVE_INDEX +#pragma GCC poison HAVE_INTP_ACCEPT +#pragma GCC poison HAVE_INTP_GETSOCKOPT #pragma GCC poison HAVE_IOMANIP_H #pragma GCC poison HAVE_IOSTREAM_H #pragma GCC poison HAVE_IOS_NOCREATE +#pragma GCC poison HAVE_ITOA #pragma GCC poison HAVE_LIBIOSTREAM +#pragma GCC poison HAVE_LIBNSL +#pragma GCC poison HAVE_LIBSOCKET +#pragma GCC poison HAVE_LISTEN #pragma GCC poison HAVE_LONGLONG +#pragma GCC poison HAVE_MALLOC_H #pragma GCC poison HAVE_MEMORY_H #pragma GCC poison HAVE_NDIR_H #pragma GCC poison HAVE_NEW_H #pragma GCC poison HAVE_OLD_READDIR_R +#pragma GCC poison HAVE_PROTOTYPE_FINITE +#pragma GCC poison HAVE_PROTOTYPE_MKSTEMP +#pragma GCC poison HAVE_PROTOTYPE_NANOSLEEP #pragma GCC poison HAVE_PROTOTYPE_STD__FINITE #pragma GCC poison HAVE_PROTOTYPE_WAIT3 +#pragma GCC poison HAVE_PROTOTYPE_FEENABLEEXCEPT +#pragma GCC poison HAVE_PROTOTYPE__FPCLASSF +#pragma GCC poison HAVE_RINDEX +#pragma GCC poison HAVE_SIGJMP_BUF #pragma GCC poison HAVE_SSTREAM_H #pragma GCC poison HAVE_STAT_H #pragma GCC poison HAVE_STREAMBUF_H #pragma GCC poison HAVE_STRSTREAM_H #pragma GCC poison HAVE_STRSTREA_H +#pragma GCC poison HAVE_SYSTEM_ERROR +#pragma GCC poison HAVE_SYS_ERRNO_H #pragma GCC poison HAVE_SYS_NDIR_H +#pragma GCC poison HAVE_TUPLE +#pragma GCC poison HAVE_UCHAR_TYPEDEF +#pragma GCC poison HAVE_UINT_TYPEDEF #pragma GCC poison HAVE_ULONGLONG +#pragma GCC poison HAVE_ULONG_TYPEDEF +#pragma GCC poison HAVE_USHORT_TYPEDEF #pragma GCC poison HAVE_WAIT3 #pragma GCC poison INCLUDE_LIBC_H_AS_CXX #pragma GCC poison INCLUDE_MATH_H_AS_CXX @@ -1099,37 +1029,68 @@ DCMTK was configured to use C++20 features, but your compiler does not or was no #pragma GCC poison TM_IN_SYS_TIME #endif +#if !defined(POISON_DEPRECATED_FEATURE_MACROS) || !defined(__GNUC__) /* #undef C_INLINE */ +/* #undef HAVE_BCMP */ /* #undef HAVE_BCOPY */ +/* #undef HAVE_CONTIGUOUS_ITERATOR_CATEGORY */ +/* #undef HAVE_DECLARATION_FP_EXCEPT_T */ /* #undef HAVE_DECLARATION_STD__IOS_BASE__OPENMODE */ /* #undef HAVE_DOPRNT */ /* #undef HAVE_EMPTY_ARGC_ARGV */ +/* #undef HAVE_FINITE */ /* #undef HAVE_FSTREAM_H */ +/* #undef HAVE_GETRUSAGE */ +/* #undef HAVE_INDEX */ +/* #undef HAVE_INTP_ACCEPT */ +/* #undef HAVE_INTP_GETSOCKOPT */ /* #undef HAVE_IOMANIP_H */ /* #undef HAVE_IOSTREAM_H */ /* #undef HAVE_IOS_NOCREATE */ +/* #undef HAVE_ITOA */ /* #undef HAVE_LIBIOSTREAM */ +/* #undef HAVE_LIBNSL */ +/* #undef HAVE_LIBSOCKET */ +/* #undef HAVE_LISTEN */ /* #undef HAVE_LONGLONG */ +/* #undef HAVE_MALLOC_H */ /* #undef HAVE_MEMORY_H */ /* #undef HAVE_NDIR_H */ /* #undef HAVE_NEW_H */ /* #undef HAVE_OLD_READDIR_R */ +/* #undef HAVE_PROTOTYPE_FINITE */ +/* #undef HAVE_PROTOTYPE_MKSTEMP */ +/* #undef HAVE_PROTOTYPE_NANOSLEEP */ /* #undef HAVE_PROTOTYPE_STD__FINITE */ /* #undef HAVE_PROTOTYPE_WAIT3 */ +/* #undef HAVE_PROTOTYPE_FEENABLEEXCEPT */ +/* #undef HAVE_PROTOTYPE__FPCLASSF */ +/* #undef HAVE_RINDEX */ +/* #undef HAVE_SIGJMP_BUF */ /* #undef HAVE_SSTREAM_H */ /* #undef HAVE_STAT_H */ /* #undef HAVE_STREAMBUF_H */ /* #undef HAVE_STRSTREAM_H */ /* #undef HAVE_STRSTREA_H */ +/* #undef HAVE_SYSTEM_ERROR */ +/* #undef HAVE_SYS_ERRNO_H */ /* #undef HAVE_SYS_NDIR_H */ +/* #undef HAVE_TUPLE */ +/* #undef HAVE_UCHAR_TYPEDEF */ +/* #undef HAVE_UINT_TYPEDEF */ /* #undef HAVE_ULONGLONG */ +/* #undef HAVE_ULONG_TYPEDEF */ +/* #undef HAVE_USHORT_TYPEDEF */ /* #undef HAVE_WAIT3 */ /* #undef INCLUDE_LIBC_H_AS_CXX */ /* #undef INCLUDE_MATH_H_AS_CXX */ /* #undef RETSIGTYPE */ /* #undef TM_IN_SYS_TIME */ /* #undef __CHAR_UNSIGNED__ */ - +#define SIZEOF_DOUBLE 8 +#define SIZEOF_FLOAT 4 +#define SIZEOF_SHORT 2 +#endif /* Historical feature tests for BSD socket functions, which exist on POSIX * systems and on Windows. These are not tested anymore. diff --git a/CMakeLists.txt b/CMakeLists.txt index d6c8ad95..5e440aa4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # Minimum CMake version required -cmake_minimum_required(VERSION 3.7.0...3.31.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.7.0...4.2.0 FATAL_ERROR) # Declare project project(DCMTK) @@ -134,6 +134,7 @@ if(CMAKE_CROSSCOMPILING) get_filename_component(FILE "${DICTIONARY}" NAME) set(DCMDICTPATH "${DCMDICTPATH}:${ANDROID_TEMPORARY_FILES_LOCATION}/${FILE}") endforeach() + set(DCMICONVPATH "${ANDROID_TEMPORARY_FILES_LOCATION}/data") configure_file("${DCMTK_SOURCE_DIR}/CMake/CTest/CTestCustomAndroid.cmake.in" "${DCMTK_BINARY_DIR}/CTestCustom.cmake" ESCAPE_QUOTES @ONLY ) diff --git a/COPYRIGHT b/COPYRIGHT index d40a73c6..bacd5c2a 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -5,7 +5,7 @@ Unless otherwise specified, the DCMTK software package has the following copyright: /* - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. * * This software and supporting documentation were developed by @@ -506,8 +506,8 @@ The dcmrt sub-package is covered by the following copyright --------------------------------------------------------------------------- -Copyright (C) 2008-2024, OFFIS e.V. and ICSMED AG, Oldenburg, Germany. -Copyright (C) 2013-2024, J. Riesmeier, Oldenburg, Germany. +Copyright (C) 2008-2025, OFFIS e.V. and ICSMED AG, Oldenburg, Germany. +Copyright (C) 2013-2025, J. Riesmeier, Oldenburg, Germany. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -543,7 +543,7 @@ Parts of the dcmsr sub-package are covered by the following copyright --------------------------------------------------------------------------- -Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany. +Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -873,7 +873,6 @@ is derived from an implementation with the following license: // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. - --------------------------------------------------------------------------- The implementation of the getLastErrorString() function in ofstub.cc @@ -882,6 +881,35 @@ the BSD license (without further specification). --------------------------------------------------------------------------- +The implementation of the JSON to DICOM converter available uses the +"jsmn" JSON parser library with the following license: + +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +--------------------------------------------------------------------------- + Finally, DCMTK can be configured and compiled to make use of a number of optional external libraries that, when available, provide added functionality such as compression, encryption, or support for certain image formats. diff --git a/CREDITS b/CREDITS index 6241d52f..63cbcb52 100644 --- a/CREDITS +++ b/CREDITS @@ -6,7 +6,7 @@ We would like to thank the following individuals, companies and organizations Open Source DICOM Toolkit DCMTK: Carl Zeiss Meditec: The preparation and publication of the DCMTK 3.6.2 release - as well as several extensions in wlmscpfs and dcmqrscp were supported by + as well as several extensions in "wlmscpfs" and "dcmqrscp" were supported by funding from Carl Zeiss Meditec AG (Muenchen, Germany). DKFZ: The work on the DCMTK module "dcmrt" was supported in part by funding @@ -17,7 +17,7 @@ Epilepsieforschung: The Gesellschaft fuer Epilepsieforschung e.V. (Bielefeld, tool "dcmrecv" and the underlying class "DcmStorageSCP". GE Aviation: GE Aviation supported DCMTK by sponsoring the development of the - dcmect library and basic concatenation support in dcmfg. + "dcmect" library and basic concatenation support in "dcmfg". ICSMED: From 2006 to 2012, the work on the DCMTK was supported by employees of the ICSMED AG (Oldenburg, Germany), a spin-off from the OFFIS institute. @@ -35,7 +35,7 @@ M. Malaterre: The GDCM developer Mathieu Malaterre (Lyon, France) regularly reports possible issues to the DCMTK team. He was also responsible for packaging the DCMTK library and tools for the Debian operating system. -Mathworks: Funded implementation of TLS support in getscu, introduced +Mathworks: Funded implementation of TLS support in "getscu", introduced in DCMTK 3.6.9. Medpace: Funded the development of the "dcm2img" tool introduced in DCMTK @@ -62,6 +62,10 @@ SlicerDMRI: The work on DCMTK Tractography modules was supported in part by initiative, to project Open Source Diffusion MRI Technology For Brain Cancer Research, award U01 CA199459. +Twisted Ceptors: Funded the extension of the "storescp" application by the + "--max-associations" option that allows the user to limit the number of + clients served in parallel when operating in "--fork mode". + VISUS: The work on various extensions of the DCMTK was supported in part by funding from the VISUS Technology Transfer GmbH (Bochum, Germany). @@ -72,4 +76,4 @@ YXLON: The work on the initial version of the DICONDE data dictionary was Please note that this list does not claim to be exhaustive. Just send us an email if you think that you or your company/organization should also be listed. -DCMTK Team, 2024-11-21 +DCMTK Team, 2025-12-15 diff --git a/INSTALL b/INSTALL index 7de38bfb..d51d541a 100644 --- a/INSTALL +++ b/INSTALL @@ -9,7 +9,7 @@ PRE-REQUISITES The DICOM toolkit (DCMTK) needs to be compiled with a C++ compiler. We recommend using the GNU C++ compiler in versions higher than 9.5.0 (most of the development for this release was done using GNU gcc 12.2.0 on Debian Linux and -GNU gcc 13.2.0 on Ubuntu Linux). The software is also known to compile using +GNU gcc 13.3.0 on Ubuntu Linux). The software is also known to compile using Clang and Microsoft Visual Studio. Compatibility with other C++ compilers is unknown, however, we have tried to @@ -28,7 +28,7 @@ Microsoft Windows The DCMTK software can be compiled under a native Microsoft Windows environment (see section "Microsoft Windows with CMake" below for more information). -The current DCMTK software release 3.6.9 successfully compiles on the following +The current DCMTK software release 3.7.0 successfully compiles on the following operating system / hardware / compiler combinations: Windows 10 / Intel x86 / Microsoft Visual C++ 2017 Community (VS 15) @@ -54,10 +54,57 @@ operating system / hardware / compiler combinations: Unix (or lookalikes) -------------------- -The current DCMTK software release 3.6.9 successfully compiles on the following +The current DCMTK software release 3.7.0 successfully compiles on the following operating system / hardware / compiler combinations using the instructions given below: + FreeBSD 14.3 / amd64|x86_64 / Clang 19.1.7 + Linux 5.14.0 / amd64|x86_64 / GNU gcc 11.5.0 (AlmaLinux 9.6) + Linux 6.1.0 / Intel x86 / Clang 14.0.6 (Debian 12) + Linux 6.1.0 / Intel x86 / GNU gcc 12.2.0 (Debian 12) + Linux 6.1.0 / amd64|x86_64 / Clang 14.0.6 (Debian 12) + Linux 6.1.0 / amd64|x86_64 / GNU gcc 12.2.0 (Debian 12) + Linux 6.1.0 / amd64|x86_64 / GNU gcc 13.2.1 (Alpine 3.20 musl libc) + Linux 6.8.0 / amd64|x86_64 / GNU gcc 11.5.0 (Ubuntu 24.04.3 LTS) + Linux 6.8.0 / amd64|x86_64 / GNU gcc 12.4.0 (Ubuntu 24.04.3 LTS) + Linux 6.8.0 / amd64|x86_64 / GNU gcc 13.3.0 (Ubuntu 24.04.3 LTS) + Linux 6.8.0 / amd64|x86_64 / GNU gcc 14.2.0 (Ubuntu 24.04.3 LTS) + Linux 6.8.0 / amd64|x86_64 / Clang 18.1.3 (Ubuntu 24.04.3 LTS) + Linux 6.8.0 / s390x / GNU gcc 13.3.0 (Ubuntu 24.04.2 LTS) + Linux 6.8.0 / s390x / Clang 18.1.3 (Ubuntu 24.04.2 LTS) + MacOS X 15.5 / amd64|x86_64 / Apple Clang 17.0.0 + MacOS X 15.5 / amd64|x86_64 / GNU gcc 15.2.0 + MacOS X 15.5 / arm64 / Apple Clang 17.0.0 + MacOS X 15.5 / arm64 / GNU gcc 15.2.0 + NetBSD 10.1 / amd64|x86_64 / Clang 18.1.8 + NetBSD 10.1 / amd64|x86_64 / GNU gcc 10.5.0 + OpenBSD 7.7 / amd64|x86_64 / Clang 16.0.6 + OpenIndiana / amd64|x86_64 / GNU gcc 14.3.0 (OpenIndiana 2025.06) + + +Cross Compiling +--------------- + +For target platforms other than Android, see section "CROSS COMPILING WITH +CMAKE" below. For Android, the current DCMTK release can be cross-compiled +targeting the following platform: + + Android / arm64 / Clang 18.0.1 (API 35, ABI arm64-v8a) + +Cross compiling support with running configuration and unit tests is provided +using CMake and requires the use of the Android emulator or Wine when targeting +Android or Windows respectively. Other versions of Android may also work, +but the above mentioned one is currently the only one that is being +regularly tested. + + +Other Platforms +--------------- + +The previous release DCMTK 3.6.9 was also tested on the following platforms +that may still work, but were not tested again for this release: + + Android / arm64 / GNU gcc 12.2.0 (API 24, ABI arm64-v8a) FreeBSD 14.1 / amd64|x86_64 / Clang 18.1.5 Linux 4.19.0 / ppc64le / IBM XL C/C++ 16.1.1 Community Linux 5.14.0 / amd64|x86_64 / GNU gcc 11.4.1 (AlmaLinux 9.4) @@ -81,25 +128,6 @@ given below: OpenBSD 7.5 / amd64|x86_64 / Clang 16.0.6 OpenIndiana / amd64|x86_64 / GNU gcc 13.2.0 (OpenIndiana 2023.10) - -Cross Compiling ---------------- - -For target platforms other than Android, see section "CROSS COMPILING WITH -CMAKE" below. For Android, the current DCMTK release can be cross-compiled -targeting the following platform: - - Android / arm64 / GNU gcc 12.2.0 (API 24, ABI arm64-v8a) - -Cross compiling support with running configuration and unit tests is provided -using CMake and requires the use of the Android emulator or Wine when targeting -Android or Windows respectively. Other versions of Android will most likely -also work, but the above mentioned one is currently the only one that is being -regularly tested. - -Other Platforms ---------------- - The previous release DCMTK 3.6.8 was also tested on the following platforms that may still work, but were not tested again for this release: @@ -134,33 +162,6 @@ that may still work, but were not tested again for this release: Windows 7 / amd64|x86_64 / Microsoft Visual C++ 2019 Community (VS 16) Windows 7 / amd64|x86_64 / MinGW gcc 9.2.0 (x86_64-w64-mingw32) -The previous release DCMTK 3.6.7 was also tested on the following platforms -that may still work, but were not tested again for this release: - - Windows 7 / Intel x86 / Microsoft Visual C++ 2010 Express (VS 10) - Windows 7 / amd64|x86_64 / Microsoft Visual C++ 2010 Express (VS 10) - Windows 7 / amd64|x86_64 / Cygwin GCC 7.4.0 (x86_64-pc-cygwin) - Windows 10 / Intel x86 / MinGW gcc 11.2.0 (i686-w64-mingw32) - Windows 10 / Intel x86 / MinGW Clang 13.0.0 (i686-w64-windows-gnu) - Windows 10 / amd64|x86_64 / MinGW gcc 11.2.0 (x86_64-w64-mingw32) - Windows 10 / amd64|x86_64 / MinGW Clang 13.0.0 (x86_64-w64-windows-gnu) - FreeBSD 13.0 / amd64|x86_64 / Clang 11.0.1 - Linux 4.19.0 / amd64|x86_64 / Clang 7.0.1 (Debian 10) - Linux 4.19.0 / amd64|x86_64 / GNU 10.3.1 (Alpine 3.15.0 with musl libc) - Linux 4.19.0 / amd64|x86_64 / GNU gcc 8.3.0 (Debian 10) - Linux 4.19.0 / amd64|x86_64 / GNU gcc 8.3.0 (Debian 10) - Linux 5.10.0 / Intel x86 / GNU gcc 10.2.1 (Debian 11) - Linux 5.4.0 / amd64|x86_64 / Clang 10.0.0 (Ubuntu 20.04) - Linux 5.4.0 / amd64|x86_64 / Clang 9.0.1 (Ubuntu 20.04) - Linux 5.4.0 / amd64|x86_64 / GNU gcc 10.3.0 (Ubuntu 20.04) - Linux 5.4.0 / amd64|x86_64 / GNU gcc 9.3.0 (Ubuntu 20.04) - Linux 5.13.0 / amd64|x86_64 / Clang 13.0.0-2 (Ubuntu 21.10) - Linux 5.13.0 / amd64|x86_64 / GNU gcc 11.2.0 (Ubuntu 21.10) - MacOS X 10.15 / amd64|x86_64 / Apple Clang 11.0.0 - MacOS X 10.15 / amd64|x86_64 / GNU gcc 9.2.0 - NetBSD 9.0 / amd64|x86_64 / Clang 10.0.1 - NetBSD 9.0 / amd64|x86_64 / GNU gcc 7.5.0 - OpenBSD 7.0 / amd64|x86_64 / Clang 11.1.1 Earlier releases of the DCMTK are known to also compile on further platforms, which are not available to us for testing purposes any more, e.g. AIX, HP-UX, @@ -199,7 +200,7 @@ underlying cryptographic routines and the TLS protocol implementation. This release of DCMTK requires OpenSSL release 1.1.1 or newer. Users should make sure that the most recent OpenSSL patch level is applied. This release of -DCMTK is known to compile with OpenSSL releases 1.1.1 as well as 3.0.x to 3.4.x. +DCMTK is known to compile with OpenSSL releases 1.1.1 as well as 3.0.x to 3.6.x. When using CMake, if support for security enhancements is desired, a compiled version of the OpenSSL libraries and include files must be available during @@ -244,7 +245,7 @@ Libtiff Support DCMTK supports the conversion of DICOM images to TIFF. DCMTK relies on the libtiff toolkit (www.libtiff.org) for this purpose. This release of DCMTK is -known to compile with libtiff releases 4.5.0 to 4.7.0, although other releases +known to compile with libtiff release 4.7.1, although other releases may work as well. When using CMake, a compiled version of the libtiff libraries and include files @@ -256,7 +257,7 @@ Libpng Support DCMTK supports the conversion of DICOM images to PNG. DCMTK relies on the libpng toolkit (www.libpng.org) for this purpose. This release of DCMTK is -known to compile with libpng releases 1.6.39 to 1.6.44, although other releases +known to compile with libpng releases 1.6.47 to 1.6.51, although other releases may work as well. When using CMake, a compiled version of the libpng libraries and include files @@ -268,7 +269,7 @@ Libxml2 Support DCMTK supports the conversion of XML documents to DICOM files. DCMTK relies on the libxml2 toolkit (www.libxml.org) for this purpose. This release of DCMTK -is known to compile with libxml2 releases 2.9.14 to 2.13.4, although other +is known to compile with libxml2 releases 2.13.9 to 2.15.1, although other releases may work as well. When using CMake, if support for XML import is desired, a compiled version of @@ -396,13 +397,16 @@ variable "CMAKE_CXX_STANDARD". By default, DCMTK will compile with "CMAKE_CXX_STANDARD" set to "11" (CXX11). The user can choose to build with a newer C++ standard by setting "CMAKE_CXX_STANDARD" accordingly. The following versions are supported: + - 11 (CXX11) - 14 (CXX14) - 17 (CXX17) - 20 (CXX20) -DCMTK will still compile with "CMAKE_CXX_STANDARD" set to "98" (CXX98), but -support will be removed in future versions of DCMTK. +Compilation of DCMTK with C++98 is deprecated. In this release, it can be +enabled by setting "CMAKE_CXX_STANDARD=98" and "DCMTK_PERMIT_CXX98=ON", but +support for C++ releases before C++11 will be removed in future versions +of DCMTK. Using C++11 or later will change some parts of DCMTK's API, so a C++11 build of DCMTK is potentially incompatible with a classic (C++98) build of DCMTK. This @@ -431,8 +435,8 @@ directories. In detail, these "CMakeLists.txt" files will serve as an input to CMake which will generate suitable build files for all of DCMTK's projects from these files. -DCMTK 3.6.9 requires CMake version 3.7.0 or later. We recommend using the -latest stable release of CMake (currently version 3.31.2) since newer versions +DCMTK 3.7.0 requires CMake version 3.7.0 or later. We recommend using the +latest stable release of CMake (currently version 4.2.0) since newer versions of CMake often provide better output in case of errors and are generally easier to use (for example, by providing better support for detecting the availability of third-party libraries). If possible, use the CMake version your operating @@ -532,7 +536,7 @@ Through CMake, perform the following steps: 1. Go to Start -> Programs -> CMake -> "CMake" or "CMake (cmake-gui)" to start the CMake utility through which the configuration can be done. 2. In the entry field "Where is the source code:" enter the directory in which - the DCMTK source code resides, e.g. "C:\dcmtk-3.6.9". + the DCMTK source code resides, e.g. "C:\dcmtk-3.7.0". 3. In the entry field "Where to build the binaries:" enter the directory in which the libraries and binaries are to be built, e.g. "C:\dcmtk-msvc16". 4. In the combobox "Build for:" or "Specify the generator for this project:" @@ -546,43 +550,38 @@ Through CMake, perform the following steps: example, in order to turn on libxml2 support, set the value of variable "DCMTK_WITH_XML" to "ON" and set the value of variable "WITH_LIBXMLINC" to the path where the include files and libraries of libxml2 can be found, e.g. - "C:\libxml2-2.13.4". The support of all other external libraries can be + "C:\libxml2-2.15.1". The support of all other external libraries can be turned on in a similar way: libpng support: set "DCMTK_WITH_PNG" to "ON" and - set "WITH_LIBPNGINC" e.g. to "C:\libpng-1.6.44" + set "WITH_LIBPNGINC" e.g. to "C:\libpng-1.6.51" libtiff support: set "DCMTK_WITH_TIFF" to "ON" and - set "WITH_LIBTIFFINC" e.g. to "C:\libtiff-4.7.0" + set "WITH_LIBTIFFINC" e.g. to "C:\libtiff-4.7.1" OpenSSL support: set "DCMTK_WITH_OPENSSL" to "ON" and - set "WITH_OPENSSLINC" e.g. to "C:\openssl-3.4.0" + set "WITH_OPENSSLINC" e.g. to "C:\openssl-3.6.0" zlib support: set "DCMTK_WITH_ZLIB" to "ON" and set "WITH_ZLIBINC" e.g. to "C:\zlib-1.3.1" - libiconv support: - set "DCMTK_WITH_ICONV" to "ON" and - set "WITH_LIBICONVINC" e.g. to "C:\libiconv-1.17" - In order to turn the support of a certain external library off, set the value of the corresponding variable ("DCMTK_WITH_XML", "DCMTK_WITH_PNG", - "DCMTK_WITH_TIFF", "DCMTK_WITH_OPENSSL", "DCMTK_WITH_ZLIB" or - "DCMTK_WITH_ICONV") to "OFF". + "DCMTK_WITH_TIFF", "DCMTK_WITH_OPENSSL" or "DCMTK_WITH_ZLIB") to "OFF". (Please note that the include files of all external libraries are always expected in a directory named "include" below the directory that is specified in "WITH_LIBXMLINC", "WITH_LIBPNGINC", "WITH_LIBTIFFINC", - "WITH_OPENSSLINC", "WITH_ZLIBINC" or "WITH_LIBICONVINC".) + "WITH_OPENSSLINC" or "WITH_ZLIBINC".) (Please note also that the library files of all external libraries are always expected in directory named "lib" below the directory that is specified in - "WITH_LIBXMLINC", "WITH_LIBPNGINC", "WITH_LIBTIFFINC", "WITH_OPENSSLINC", - "WITH_ZLIBINC" or "WITH_LIBICONV". Moreover, note that the following + "WITH_LIBXMLINC", "WITH_LIBPNGINC", "WITH_LIBTIFFINC", "WITH_OPENSSLINC" + or "WITH_ZLIBINC". Moreover, note that the following filenames must be used for the corresponding lib files: libxml2: @@ -609,21 +608,16 @@ Through CMake, perform the following steps: "zlib_d.lib" - debug version "zlib_o.lib" - release version (optimized) - libiconv: (* See note below) - "libiconv_d.lib" - debug version - "libiconv_o.lib" - release version (optimized) - "libchset_d.lib" - debug version - "libchset_o.lib" - release version (optimized) + The debug and release versions of all libraries must be compiled with the + same runtime library dependency as DCMTK (i.e., /MT[d] or /MD[d]). + By default, DCMTK is compiled with /MTd for Debug and /MT for Release mode + unless the CMake variables "DCMTK_COMPILE_WIN32_MULTITHREADED_DLL" or + "BUILD_SHARED_LIBS" (see below) are set to true, in which case DCMTK is + compiled with /MDd for Debug and /MD for Release mode. - The debug versions of all libraries must be compiled for the multithread - debug version of the runtime (/MTd), the release version must be compiled - for the non-debug multithread runtime (/MT). Precompiled versions of all - libraries (*) can be downloaded from https://dcmtk.org/develop/#lib-win . + Precompiled versions of all libraries can be downloaded from + https://dcmtk.org/develop/#lib-win . - (*) Starting with DCMTK 3.6.8, libiconv is no longer part of the download - package. That also means that libxml2 was built without this library. - Therefore, DCMTK tools that read XML files will only support UTF-8 and - Latin-1 encoding. In the CMake GUI, there are a few more settings that can be modified to affect the way DCMTK is compiled. The most important of these are: @@ -698,28 +692,28 @@ see: https://cmake.org/cmake/help/latest/module/FindZLIB.html . The typical way to build DCMTK on Unix-like systems with CMake is as follows (if not using the GUI, in that case look at the description for Windows above): - mkdir dcmtk-3.6.9-build - cd dcmtk-3.6.9-build - cmake ../dcmtk-3.6.9 + mkdir dcmtk-3.7.0-build + cd dcmtk-3.7.0-build + cmake ../dcmtk-3.7.0 make -j8 - make DESTDIR=../dcmtk-3.6.9-install install + make DESTDIR=../dcmtk-3.7.0-install install The above commands assume that the DCMTK source code was extracted to the -current working directory into a folder named "dcmtk-3.6.9". DCMTK will be +current working directory into a folder named "dcmtk-3.7.0". DCMTK will be configured using CMake with the default options, detecting and including all available support libraries and then compiled using eight CPU cores ("make -j8", adjust as needed). The result will be installed to the directory -"dcmtk-3.6.9-install" next to the source code directory. +"dcmtk-3.7.0-install" next to the source code directory. If you want to modify your build configuration, like enabling or disabling some features of DCMTK (e.g. PNG support), or if you need to modify the predefined build variables, you can use the curses-based CMake configuration tool ccmake. First, create the initial build setup (system check) and then call "ccmake": - mkdir dcmtk-3.6.9-build - cd dcmtk-3.6.9-build - cmake ../dcmtk-3.6.9 - ccmake ../dcmtk-3.6.9 + mkdir dcmtk-3.7.0-build + cd dcmtk-3.7.0-build + cmake ../dcmtk-3.7.0 + ccmake ../dcmtk-3.7.0 Now you can modify the configuration values. Please see the help on the bottom of the screen. When finished, press "c" to generate a new build configuration, @@ -729,9 +723,9 @@ If you already know the variable names, types and values to set, you can skip the ccmake step above and call "cmake" directly with the desired values set. Example for a build with TCP wrapper disabled: - mkdir dcmtk-3.6.9-build - cd dcmtk-3.6.9-build - cmake -DDCMTK_WITH_WRAP:BOOL=FALSE ../dcmtk-3.6.9 + mkdir dcmtk-3.7.0-build + cd dcmtk-3.7.0-build + cmake -DDCMTK_WITH_WRAP:BOOL=FALSE ../dcmtk-3.7.0 ... The format is "NAME:TYPE=VALUE". Use ccmake to find out the variable names and @@ -759,7 +753,7 @@ HTML DOCUMENTATION AND MAN PAGES Most DCMTK modules have been documented with Doxygen (www.doxygen.org), a free source code documentation system similar to Javadoc. Unix users who have Doxygen installed can create a hypertext documentation with "make html" in the -"dcmtk-3.6.9" or "doxygen" directory; Windows and other CMake users should +"dcmtk-3.7.0" or "doxygen" directory; Windows and other CMake users should build the "DOXYGEN" target. A project file for Microsoft's HTML Help Workshop can also be generated allowing to create a single CHM file (compressed HTML) from the documentation. Other output formats (e.g. LaTeX) can be enabled by @@ -900,7 +894,7 @@ removed in a future release. In the current release, the "configure" script in DCMTK's top-level main directory has been removed as the final warning for users of the Autoconf toolchain. If you prefer to build DCMTK with Autoconf, however, this is still possible. Perform the following steps from the top-level -("dcmtk-3.6.9") directory to compile and install the software: +("dcmtk-3.7.0") directory to compile and install the software: Step 0: cd config @@ -930,14 +924,14 @@ which OpenSSL is installed. This is usually the directory that has been used as "--prefix" when compiling and installing OpenSSL. For example, if you wish to enable the security enhancements, and OpenSSL is -installed in "/usr/local/apps/openssl-3.4.0", you should start configure as: +installed in "/usr/local/apps/openssl-3.6.0", you should start configure as: ./configure --ignore-deprecation - --with-opensslinc=/usr/local/apps/openssl-3.4.0 + --with-opensslinc=/usr/local/apps/openssl-3.6.0 Configure will assume that the OpenSSL include files are installed in -"/usr/local/apps/openssl-3.4.0/include" and will expect the library in -"/usr/local/apps/openssl-3.4.0/lib". Appropriate options will be passed to +"/usr/local/apps/openssl-3.6.0/include" and will expect the library in +"/usr/local/apps/openssl-3.6.0/lib". Appropriate options will be passed to the compiler and the linker. Support for zlib, libtiff, libpng, libxml2, libwrap, and/or libiconv can be @@ -946,9 +940,9 @@ standard path), e.g.: ./configure --ignore-deprecation --with-libzlibinc=/usr/local/apps/zlib-1.3.1 - --with-libtiffinc=/usr/local/apps/libtiff-4.7.0 - --with-libpnginc=/usr/local/apps/libpng-1.6.44 - --with-libxmlinc=/usr/local/apps/libxml2-2.13.4 + --with-libtiffinc=/usr/local/apps/libtiff-4.7.1 + --with-libpnginc=/usr/local/apps/libpng-1.6.51 + --with-libxmlinc=/usr/local/apps/libxml2-2.15.1 --with-libwrapinc=/usr/local/apps/tcp_wrappers-7.6 --with-libiconvinc=/usr/local/apps/libiconv-1.17 @@ -1047,7 +1041,7 @@ See also the FAQ at https://forum.dcmtk.org/faq for more hints. Have fun. -M. Eichelberg, J. Riesmeier, M. Onken, T. Xu +M. Eichelberg, J. Riesmeier, M. Onken, T. Xu, H. Roesen DCMTK Development Team, Oldenburg, Germany. -Last revised: 2024-12-10 (Onken) +Last revised: 2025-12-02 (Riesmeier) diff --git a/Makefile b/Makefile index 44ef8d37..416c1252 100644 --- a/Makefile +++ b/Makefile @@ -9,39 +9,39 @@ include $(configdir)/Makefile.def .NOTPARALLEL: -all: config-all oficonv-all ofstd-all oflog-all dcmdata-all dcmiod-all dcmfg-all dcmseg-all dcmimgle-all dcmimage-all dcmjpeg-all dcmjpls-all dcmtls-all dcmnet-all dcmsr-all dcmsign-all dcmwlm-all dcmqrdb-all dcmpstat-all dcmrt-all dcmtract-all dcmpmap-all dcmect-all dcmapps-all +all: config-all oficonv-all ofstd-all oflog-all dcmdata-all dcmimgle-all dcmimage-all dcmjpeg-all dcmjpls-all dcmtls-all dcmnet-all dcmsr-all dcmsign-all dcmwlm-all dcmqrdb-all dcmrt-all dcmiod-all dcmpstat-all dcmfg-all dcmseg-all dcmtract-all dcmpmap-all dcmect-all dcmapps-all -libsrc-all: oficonv-libsrc-all ofstd-libsrc-all oflog-libsrc-all dcmdata-libsrc-all dcmiod-libsrc-all dcmfg-libsrc-all dcmseg-libsrc-all dcmimgle-libsrc-all dcmimage-libsrc-all dcmjpeg-libsrc-all dcmjpls-libsrc-all dcmtls-libsrc-all dcmnet-libsrc-all dcmsr-libsrc-all dcmsign-libsrc-all dcmwlm-libsrc-all dcmqrdb-libsrc-all dcmpstat-libsrc-all dcmrt-libsrc-all dcmtract-libsrc-all dcmpmap-libsrc-all dcmect-libsrc-all dcmapps-libsrc-all +libsrc-all: oficonv-libsrc-all ofstd-libsrc-all oflog-libsrc-all dcmdata-libsrc-all dcmimgle-libsrc-all dcmimage-libsrc-all dcmjpeg-libsrc-all dcmjpls-libsrc-all dcmtls-libsrc-all dcmnet-libsrc-all dcmsr-libsrc-all dcmsign-libsrc-all dcmwlm-libsrc-all dcmqrdb-libsrc-all dcmrt-libsrc-all dcmiod-libsrc-all dcmpstat-libsrc-all dcmfg-libsrc-all dcmseg-libsrc-all dcmtract-libsrc-all dcmpmap-libsrc-all dcmect-libsrc-all dcmapps-libsrc-all -tests-all: config-tests-all oficonv-tests-all ofstd-tests-all oflog-tests-all dcmdata-tests-all dcmiod-tests-all dcmfg-tests-all dcmseg-tests-all dcmimgle-tests-all dcmimage-tests-all dcmjpeg-tests-all dcmjpls-tests-all dcmtls-tests-all dcmnet-tests-all dcmsr-tests-all dcmsign-tests-all dcmwlm-tests-all dcmqrdb-tests-all dcmpstat-tests-all dcmrt-tests-all dcmtract-tests-all dcmpmap-tests-all dcmect-tests-all dcmapps-tests-all +tests-all: config-tests-all oficonv-tests-all ofstd-tests-all oflog-tests-all dcmdata-tests-all dcmimgle-tests-all dcmimage-tests-all dcmjpeg-tests-all dcmjpls-tests-all dcmtls-tests-all dcmnet-tests-all dcmsr-tests-all dcmsign-tests-all dcmwlm-tests-all dcmqrdb-tests-all dcmrt-tests-all dcmiod-tests-all dcmpstat-tests-all dcmfg-tests-all dcmseg-tests-all dcmtract-tests-all dcmpmap-tests-all dcmect-tests-all dcmapps-tests-all -install: config-install oficonv-install ofstd-install oflog-install dcmdata-install dcmiod-install dcmfg-install dcmseg-install dcmimgle-install dcmimage-install dcmjpeg-install dcmjpls-install dcmtls-install dcmnet-install dcmsr-install dcmsign-install dcmwlm-install dcmqrdb-install dcmpstat-install dcmrt-install dcmtract-install dcmpmap-install dcmect-install dcmapps-install dcmtk-install-doc install-man +install: config-install oficonv-install ofstd-install oflog-install dcmdata-install dcmimgle-install dcmimage-install dcmjpeg-install dcmjpls-install dcmtls-install dcmnet-install dcmsr-install dcmsign-install dcmwlm-install dcmqrdb-install dcmrt-install dcmiod-install dcmpstat-install dcmfg-install dcmseg-install dcmtract-install dcmpmap-install dcmect-install dcmapps-install dcmtk-install-doc install-man install-all: install install-lib install-html -install-bin: config-install-bin oficonv-install-bin ofstd-install-bin oflog-install-bin dcmdata-install-bin dcmiod-install-bin dcmfg-install-bin dcmseg-install-bin dcmimgle-install-bin dcmimage-install-bin dcmjpeg-install-bin dcmjpls-install-bin dcmtls-install-bin dcmnet-install-bin dcmsr-install-bin dcmsign-install-bin dcmwlm-install-bin dcmqrdb-install-bin dcmpstat-install-bin dcmrt-install-bin dcmtract-install-bin dcmpmap-install-bin dcmect-install-bin dcmapps-install-bin +install-bin: config-install-bin oficonv-install-bin ofstd-install-bin oflog-install-bin dcmdata-install-bin dcmimgle-install-bin dcmimage-install-bin dcmjpeg-install-bin dcmjpls-install-bin dcmtls-install-bin dcmnet-install-bin dcmsr-install-bin dcmsign-install-bin dcmwlm-install-bin dcmqrdb-install-bin dcmrt-install-bin dcmiod-install-bin dcmpstat-install-bin dcmfg-install-bin dcmseg-install-bin dcmtract-install-bin dcmpmap-install-bin dcmect-install-bin dcmapps-install-bin -install-doc: config-install-doc oficonv-install-doc ofstd-install-doc oflog-install-doc dcmdata-install-doc dcmiod-install-doc dcmfg-install-doc dcmseg-install-doc dcmimgle-install-doc dcmimage-install-doc dcmjpeg-install-doc dcmjpls-install-doc dcmtls-install-doc dcmnet-install-doc dcmsr-install-doc dcmsign-install-doc dcmwlm-install-doc dcmqrdb-install-doc dcmpstat-install-doc dcmrt-install-doc dcmtract-install-doc dcmpmap-install-doc dcmect-install-doc dcmapps-install-doc +install-doc: config-install-doc oficonv-install-doc ofstd-install-doc oflog-install-doc dcmdata-install-doc dcmimgle-install-doc dcmimage-install-doc dcmjpeg-install-doc dcmjpls-install-doc dcmtls-install-doc dcmnet-install-doc dcmsr-install-doc dcmsign-install-doc dcmwlm-install-doc dcmqrdb-install-doc dcmrt-install-doc dcmiod-install-doc dcmpstat-install-doc dcmfg-install-doc dcmseg-install-doc dcmtract-install-doc dcmpmap-install-doc dcmect-install-doc dcmapps-install-doc -install-data: config-install-data oficonv-install-data ofstd-install-data oflog-install-data dcmdata-install-data dcmiod-install-data dcmfg-install-data dcmseg-install-data dcmimgle-install-data dcmimage-install-data dcmjpeg-install-data dcmjpls-install-data dcmtls-install-data dcmnet-install-data dcmsr-install-data dcmsign-install-data dcmwlm-install-data dcmqrdb-install-data dcmpstat-install-data dcmrt-install-data dcmtract-install-data dcmpmap-install-data dcmect-install-data dcmapps-install-data +install-data: config-install-data oficonv-install-data ofstd-install-data oflog-install-data dcmdata-install-data dcmimgle-install-data dcmimage-install-data dcmjpeg-install-data dcmjpls-install-data dcmtls-install-data dcmnet-install-data dcmsr-install-data dcmsign-install-data dcmwlm-install-data dcmqrdb-install-data dcmrt-install-data dcmiod-install-data dcmpstat-install-data dcmfg-install-data dcmseg-install-data dcmtract-install-data dcmpmap-install-data dcmect-install-data dcmapps-install-data -install-etc: config-install-etc oficonv-install-etc ofstd-install-etc oflog-install-etc dcmdata-install-etc dcmiod-install-etc dcmfg-install-etc dcmseg-install-etc dcmimgle-install-etc dcmimage-install-etc dcmjpeg-install-etc dcmjpls-install-etc dcmtls-install-etc dcmnet-install-etc dcmsr-install-etc dcmsign-install-etc dcmwlm-install-etc dcmqrdb-install-etc dcmpstat-install-etc dcmrt-install-etc dcmtract-install-etc dcmpmap-install-etc dcmect-install-etc dcmapps-install-etc +install-etc: config-install-etc oficonv-install-etc ofstd-install-etc oflog-install-etc dcmdata-install-etc dcmimgle-install-etc dcmimage-install-etc dcmjpeg-install-etc dcmjpls-install-etc dcmtls-install-etc dcmnet-install-etc dcmsr-install-etc dcmsign-install-etc dcmwlm-install-etc dcmqrdb-install-etc dcmrt-install-etc dcmiod-install-etc dcmpstat-install-etc dcmfg-install-etc dcmseg-install-etc dcmtract-install-etc dcmpmap-install-etc dcmect-install-etc dcmapps-install-etc -install-lib: config-install-lib oficonv-install-lib ofstd-install-lib oflog-install-lib dcmdata-install-lib dcmiod-install-lib dcmfg-install-lib dcmseg-install-lib dcmimgle-install-lib dcmimage-install-lib dcmjpeg-install-lib dcmjpls-install-lib dcmtls-install-lib dcmnet-install-lib dcmsr-install-lib dcmsign-install-lib dcmwlm-install-lib dcmqrdb-install-lib dcmpstat-install-lib dcmrt-install-lib dcmtract-install-lib dcmpmap-install-lib dcmect-install-lib dcmapps-install-lib +install-lib: config-install-lib oficonv-install-lib ofstd-install-lib oflog-install-lib dcmdata-install-lib dcmimgle-install-lib dcmimage-install-lib dcmjpeg-install-lib dcmjpls-install-lib dcmtls-install-lib dcmnet-install-lib dcmsr-install-lib dcmsign-install-lib dcmwlm-install-lib dcmqrdb-install-lib dcmrt-install-lib dcmiod-install-lib dcmpstat-install-lib dcmfg-install-lib dcmseg-install-lib dcmtract-install-lib dcmpmap-install-lib dcmect-install-lib dcmapps-install-lib -install-include: config-install-include oficonv-install-include ofstd-install-include oflog-install-include dcmdata-install-include dcmiod-install-include dcmfg-install-include dcmseg-install-include dcmimgle-install-include dcmimage-install-include dcmjpeg-install-include dcmjpls-install-include dcmtls-install-include dcmnet-install-include dcmsr-install-include dcmsign-install-include dcmwlm-install-include dcmqrdb-install-include dcmpstat-install-include dcmrt-install-include dcmtract-install-include dcmpmap-install-include dcmect-install-include dcmapps-install-include +install-include: config-install-include oficonv-install-include ofstd-install-include oflog-install-include dcmdata-install-include dcmimgle-install-include dcmimage-install-include dcmjpeg-install-include dcmjpls-install-include dcmtls-install-include dcmnet-install-include dcmsr-install-include dcmsign-install-include dcmwlm-install-include dcmqrdb-install-include dcmrt-install-include dcmiod-install-include dcmpstat-install-include dcmfg-install-include dcmseg-install-include dcmtract-install-include dcmpmap-install-include dcmect-install-include dcmapps-install-include -install-support: config-install-support oficonv-install-support ofstd-install-support oflog-install-support dcmdata-install-support dcmiod-install-support dcmfg-install-support dcmseg-install-support dcmimgle-install-support dcmimage-install-support dcmjpeg-install-support dcmjpls-install-support dcmtls-install-support dcmnet-install-support dcmsr-install-support dcmsign-install-support dcmwlm-install-support dcmqrdb-install-support dcmpstat-install-support dcmrt-install-support dcmtract-install-support dcmpmap-install-support dcmect-install-support dcmapps-install-support +install-support: config-install-support oficonv-install-support ofstd-install-support oflog-install-support dcmdata-install-support dcmimgle-install-support dcmimage-install-support dcmjpeg-install-support dcmjpls-install-support dcmtls-install-support dcmnet-install-support dcmsr-install-support dcmsign-install-support dcmwlm-install-support dcmqrdb-install-support dcmrt-install-support dcmiod-install-support dcmpstat-install-support dcmfg-install-support dcmseg-install-support dcmtract-install-support dcmpmap-install-support dcmect-install-support dcmapps-install-support check: tests-all $(MAKE) -s check-nosilent -check-nosilent: config-check oficonv-check ofstd-check oflog-check dcmdata-check dcmiod-check dcmfg-check dcmseg-check dcmimgle-check dcmimage-check dcmjpeg-check dcmjpls-check dcmtls-check dcmnet-check dcmsr-check dcmsign-check dcmwlm-check dcmqrdb-check dcmpstat-check dcmrt-check dcmtract-check dcmpmap-check dcmect-check dcmapps-check +check-nosilent: config-check oficonv-check ofstd-check oflog-check dcmdata-check dcmimgle-check dcmimage-check dcmjpeg-check dcmjpls-check dcmtls-check dcmnet-check dcmsr-check dcmsign-check dcmwlm-check dcmqrdb-check dcmrt-check dcmiod-check dcmpstat-check dcmfg-check dcmseg-check dcmtract-check dcmpmap-check dcmect-check dcmapps-check check-exhaustive: tests-all $(MAKE) -s check-nosilent-exhaustive -check-nosilent-exhaustive: config-check-exhaustive oficonv-check-exhaustive ofstd-check-exhaustive oflog-check-exhaustive dcmdata-check-exhaustive dcmiod-check-exhaustive dcmfg-check-exhaustive dcmseg-check-exhaustive dcmimgle-check-exhaustive dcmimage-check-exhaustive dcmjpeg-check-exhaustive dcmjpls-check-exhaustive dcmtls-check-exhaustive dcmnet-check-exhaustive dcmsr-check-exhaustive dcmsign-check-exhaustive dcmwlm-check-exhaustive dcmqrdb-check-exhaustive dcmpstat-check-exhaustive dcmrt-check-exhaustive dcmtract-check-exhaustive dcmpmap-check-exhaustive dcmect-check-exhaustive dcmapps-check-exhaustive +check-nosilent-exhaustive: config-check-exhaustive oficonv-check-exhaustive ofstd-check-exhaustive oflog-check-exhaustive dcmdata-check-exhaustive dcmimgle-check-exhaustive dcmimage-check-exhaustive dcmjpeg-check-exhaustive dcmjpls-check-exhaustive dcmtls-check-exhaustive dcmnet-check-exhaustive dcmsr-check-exhaustive dcmsign-check-exhaustive dcmwlm-check-exhaustive dcmqrdb-check-exhaustive dcmrt-check-exhaustive dcmiod-check-exhaustive dcmpstat-check-exhaustive dcmfg-check-exhaustive dcmseg-check-exhaustive dcmtract-check-exhaustive dcmpmap-check-exhaustive dcmect-check-exhaustive dcmapps-check-exhaustive dcmtk-install-doc: $(configdir)/mkinstalldirs $(DESTDIR)$(docdir) @@ -91,7 +91,7 @@ help: @echo "" @echo "The following modules are available:" @echo "" - @echo "oficonv ofstd oflog dcmdata dcmiod dcmfg dcmseg dcmimgle dcmimage dcmjpeg dcmjpls dcmtls dcmnet dcmsr dcmsign dcmwlm dcmqrdb dcmpstat dcmrt dcmtract dcmpmap dcmect dcmapps" + @echo "oficonv ofstd oflog dcmdata dcmimgle dcmimage dcmjpeg dcmjpls dcmtls dcmnet dcmsr dcmsign dcmwlm dcmqrdb dcmrt dcmiod dcmpstat dcmfg dcmseg dcmtract dcmpmap dcmect dcmapps" config-all: (cd config && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" all) @@ -288,123 +288,6 @@ dcmdata-check: dcmdata-check-exhaustive: (cd dcmdata && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check-exhaustive) -dcmiod-all: - (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" all) - -dcmiod-libsrc-all: - (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" libsrc-all) - -dcmiod-tests-all: - (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" tests-all) - -dcmiod-install: - (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install) - -dcmiod-install-bin: - (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-bin) - -dcmiod-install-doc: - (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-doc) - -dcmiod-install-data: - (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-data) - -dcmiod-install-etc: - (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-etc) - -dcmiod-install-lib: - (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-lib) - -dcmiod-install-include: - (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-include) - -dcmiod-install-support: - (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-support) - -dcmiod-check: - (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check) - -dcmiod-check-exhaustive: - (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check-exhaustive) - -dcmfg-all: - (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" all) - -dcmfg-libsrc-all: - (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" libsrc-all) - -dcmfg-tests-all: - (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" tests-all) - -dcmfg-install: - (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install) - -dcmfg-install-bin: - (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-bin) - -dcmfg-install-doc: - (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-doc) - -dcmfg-install-data: - (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-data) - -dcmfg-install-etc: - (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-etc) - -dcmfg-install-lib: - (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-lib) - -dcmfg-install-include: - (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-include) - -dcmfg-install-support: - (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-support) - -dcmfg-check: - (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check) - -dcmfg-check-exhaustive: - (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check-exhaustive) - -dcmseg-all: - (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" all) - -dcmseg-libsrc-all: - (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" libsrc-all) - -dcmseg-tests-all: - (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" tests-all) - -dcmseg-install: - (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install) - -dcmseg-install-bin: - (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-bin) - -dcmseg-install-doc: - (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-doc) - -dcmseg-install-data: - (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-data) - -dcmseg-install-etc: - (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-etc) - -dcmseg-install-lib: - (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-lib) - -dcmseg-install-include: - (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-include) - -dcmseg-install-support: - (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-support) - -dcmseg-check: - (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check) - -dcmseg-check-exhaustive: - (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check-exhaustive) - dcmimgle-all: (cd dcmimgle && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" all) @@ -795,6 +678,84 @@ dcmqrdb-check: dcmqrdb-check-exhaustive: (cd dcmqrdb && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check-exhaustive) +dcmrt-all: + (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" all) + +dcmrt-libsrc-all: + (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" libsrc-all) + +dcmrt-tests-all: + (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" tests-all) + +dcmrt-install: + (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install) + +dcmrt-install-bin: + (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-bin) + +dcmrt-install-doc: + (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-doc) + +dcmrt-install-data: + (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-data) + +dcmrt-install-etc: + (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-etc) + +dcmrt-install-lib: + (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-lib) + +dcmrt-install-include: + (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-include) + +dcmrt-install-support: + (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-support) + +dcmrt-check: + (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check) + +dcmrt-check-exhaustive: + (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check-exhaustive) + +dcmiod-all: + (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" all) + +dcmiod-libsrc-all: + (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" libsrc-all) + +dcmiod-tests-all: + (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" tests-all) + +dcmiod-install: + (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install) + +dcmiod-install-bin: + (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-bin) + +dcmiod-install-doc: + (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-doc) + +dcmiod-install-data: + (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-data) + +dcmiod-install-etc: + (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-etc) + +dcmiod-install-lib: + (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-lib) + +dcmiod-install-include: + (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-include) + +dcmiod-install-support: + (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-support) + +dcmiod-check: + (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check) + +dcmiod-check-exhaustive: + (cd dcmiod && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check-exhaustive) + dcmpstat-all: (cd dcmpstat && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" all) @@ -834,44 +795,83 @@ dcmpstat-check: dcmpstat-check-exhaustive: (cd dcmpstat && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check-exhaustive) -dcmrt-all: - (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" all) +dcmfg-all: + (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" all) -dcmrt-libsrc-all: - (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" libsrc-all) +dcmfg-libsrc-all: + (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" libsrc-all) -dcmrt-tests-all: - (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" tests-all) +dcmfg-tests-all: + (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" tests-all) -dcmrt-install: - (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install) +dcmfg-install: + (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install) -dcmrt-install-bin: - (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-bin) +dcmfg-install-bin: + (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-bin) -dcmrt-install-doc: - (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-doc) +dcmfg-install-doc: + (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-doc) -dcmrt-install-data: - (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-data) +dcmfg-install-data: + (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-data) -dcmrt-install-etc: - (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-etc) +dcmfg-install-etc: + (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-etc) -dcmrt-install-lib: - (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-lib) +dcmfg-install-lib: + (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-lib) -dcmrt-install-include: - (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-include) +dcmfg-install-include: + (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-include) -dcmrt-install-support: - (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-support) +dcmfg-install-support: + (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-support) -dcmrt-check: - (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check) +dcmfg-check: + (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check) -dcmrt-check-exhaustive: - (cd dcmrt && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check-exhaustive) +dcmfg-check-exhaustive: + (cd dcmfg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check-exhaustive) + +dcmseg-all: + (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" all) + +dcmseg-libsrc-all: + (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" libsrc-all) + +dcmseg-tests-all: + (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" tests-all) + +dcmseg-install: + (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install) + +dcmseg-install-bin: + (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-bin) + +dcmseg-install-doc: + (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-doc) + +dcmseg-install-data: + (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-data) + +dcmseg-install-etc: + (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-etc) + +dcmseg-install-lib: + (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-lib) + +dcmseg-install-include: + (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-include) + +dcmseg-install-support: + (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" install-support) + +dcmseg-check: + (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check) + +dcmseg-check-exhaustive: + (cd dcmseg && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" check-exhaustive) dcmtract-all: (cd dcmtract && $(MAKE) ARCH="$(ARCH)" DESTDIR="$(DESTDIR)" all) @@ -1035,9 +1035,6 @@ dependencies: (cd ofstd && $(MAKE) dependencies) (cd oflog && $(MAKE) dependencies) (cd dcmdata && $(MAKE) dependencies) - (cd dcmiod && $(MAKE) dependencies) - (cd dcmfg && $(MAKE) dependencies) - (cd dcmseg && $(MAKE) dependencies) (cd dcmimgle && $(MAKE) dependencies) (cd dcmimage && $(MAKE) dependencies) (cd dcmjpeg && $(MAKE) dependencies) @@ -1048,8 +1045,11 @@ dependencies: (cd dcmsign && $(MAKE) dependencies) (cd dcmwlm && $(MAKE) dependencies) (cd dcmqrdb && $(MAKE) dependencies) - (cd dcmpstat && $(MAKE) dependencies) (cd dcmrt && $(MAKE) dependencies) + (cd dcmiod && $(MAKE) dependencies) + (cd dcmpstat && $(MAKE) dependencies) + (cd dcmfg && $(MAKE) dependencies) + (cd dcmseg && $(MAKE) dependencies) (cd dcmtract && $(MAKE) dependencies) (cd dcmpmap && $(MAKE) dependencies) (cd dcmect && $(MAKE) dependencies) @@ -1060,9 +1060,6 @@ clean: (cd ofstd && $(MAKE) clean) (cd oflog && $(MAKE) clean) (cd dcmdata && $(MAKE) clean) - (cd dcmiod && $(MAKE) clean) - (cd dcmfg && $(MAKE) clean) - (cd dcmseg && $(MAKE) clean) (cd dcmimgle && $(MAKE) clean) (cd dcmimage && $(MAKE) clean) (cd dcmjpeg && $(MAKE) clean) @@ -1073,8 +1070,11 @@ clean: (cd dcmsign && $(MAKE) clean) (cd dcmwlm && $(MAKE) clean) (cd dcmqrdb && $(MAKE) clean) - (cd dcmpstat && $(MAKE) clean) (cd dcmrt && $(MAKE) clean) + (cd dcmiod && $(MAKE) clean) + (cd dcmpstat && $(MAKE) clean) + (cd dcmfg && $(MAKE) clean) + (cd dcmseg && $(MAKE) clean) (cd dcmtract && $(MAKE) clean) (cd dcmpmap && $(MAKE) clean) (cd dcmect && $(MAKE) clean) @@ -1088,9 +1088,6 @@ distclean: (cd ofstd && $(MAKE) distclean) (cd oflog && $(MAKE) distclean) (cd dcmdata && $(MAKE) distclean) - (cd dcmiod && $(MAKE) distclean) - (cd dcmfg && $(MAKE) distclean) - (cd dcmseg && $(MAKE) distclean) (cd dcmimgle && $(MAKE) distclean) (cd dcmimage && $(MAKE) distclean) (cd dcmjpeg && $(MAKE) distclean) @@ -1101,8 +1098,11 @@ distclean: (cd dcmsign && $(MAKE) distclean) (cd dcmwlm && $(MAKE) distclean) (cd dcmqrdb && $(MAKE) distclean) - (cd dcmpstat && $(MAKE) distclean) (cd dcmrt && $(MAKE) distclean) + (cd dcmiod && $(MAKE) distclean) + (cd dcmpstat && $(MAKE) distclean) + (cd dcmfg && $(MAKE) distclean) + (cd dcmseg && $(MAKE) distclean) (cd dcmtract && $(MAKE) distclean) (cd dcmpmap && $(MAKE) distclean) (cd dcmect && $(MAKE) distclean) diff --git a/VERSION b/VERSION index cff2619c..7c69a55d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.6.9 +3.7.0 diff --git a/config/aclocal.m4 b/config/aclocal.m4 index 38d2e6c1..ed66314c 100644 --- a/config/aclocal.m4 +++ b/config/aclocal.m4 @@ -344,30 +344,6 @@ fi rm -f conftest*]) -dnl AC_CHECK_STD_NAMESPACE checks if the C++-Compiler supports the -dnl standard name space. -dnl -dnl AC_CHECK_STD_NAMESPACE -AC_DEFUN(AC_CHECK_STD_NAMESPACE, -[AC_MSG_CHECKING([for C++ standard namespace]) -AH_TEMPLATE(HAVE_STD_NAMESPACE, [Define if ANSI standard C++ includes use std namespace.]) -AC_CACHE_VAL(ac_cv_check_std_namespace, -[AC_TRY_COMPILE_AND_LINK([ -#include -using namespace std; -],[ - cout << "Hello World" << endl; -], eval "ac_cv_check_std_namespace=yes", eval "ac_cv_check_std_namespace=no")dnl -])dnl -if eval "test \"`echo '$ac_cv_check_std_namespace'`\" = yes"; then - AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_STD_NAMESPACE) -else - AC_MSG_RESULT(no) -fi -]) - - dnl AC_CHECK_GNU_LIBTOOL checks whether libtool is GNU libtool. dnl This macro requires that 'libtool' exists in the current path, dnl i.e. AC_CHECK_PROGS(LIBTOOL, libtool, :) should be executed and evaluated @@ -514,75 +490,6 @@ fi ]) -dnl AC_CHECK_INTP_ACCEPT checks if the prototype for accept() -dnl specifies arguments 2-4 to be int* instead of size_t *. -dnl -dnl AC_CHECK_INTP_ACCEPT(HEADER-FILE..., ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) -AC_DEFUN(AC_CHECK_INTP_ACCEPT, -[AC_MSG_CHECKING([ifelse([$1], , [if accept() needs int* parameters], -[if accept() needs int* parameters (in $1)])]) -AH_TEMPLATE(HAVE_INTP_ACCEPT, [Define if your system declares argument 3 of accept() - as int * instead of size_t * or socklen_t *.]) -ifelse([$1], , [ac_includes="" -], -[ac_includes="" -for ac_header in $1 -do - ac_includes="$ac_includes -#include<$ac_header>" -done]) -AC_CACHE_VAL(ac_cv_prototype_intp_accept, -[AC_TRY_COMPILE( -[#ifdef __cplusplus -extern "C" { -#endif -$ac_includes -#ifdef __cplusplus -} -#endif -] -, -[ - int i; - struct sockaddr *addr; - size_t addrlen; - - addr = 0; - addrlen = 0; - i = accept(1, addr, &addrlen); -], -eval "ac_cv_prototype_intp_accept=no", -[AC_TRY_COMPILE( -[#ifdef __cplusplus -extern "C" { -#endif -$ac_includes -#ifdef __cplusplus -} -#endif -] -, -[ - int i; - struct sockaddr *addr; - int addrlen; - - addr = 0; - addrlen = 0; - i = accept(1, addr, &addrlen); -], -eval "ac_cv_prototype_intp_accept=yes", eval "ac_cv_prototype_intp_accept=no")])]) -if eval "test \"`echo $ac_cv_prototype_intp_accept`\" = yes"; then - AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_INTP_ACCEPT) - ifelse([$2], , :, [$2]) -else - AC_MSG_RESULT(no) - ifelse([$3], , , [$3]) -fi -]) - - dnl AC_CHECK_PTHREAD_OPTION checks whether the compiler requires the dnl -pthread option to correctly link code containing posix thread calls. dnl This is true for example on FreeBSD. @@ -680,105 +587,6 @@ fi ]) -dnl AC_CHECK_INTP_GETSOCKOPT checks if the prototype for getsockopt() -dnl specifies arguments 5 to be int* instead of size_t *. -dnl -dnl AC_CHECK_INTP_GETSOCKOPT(HEADER-FILE..., ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) -AC_DEFUN(AC_CHECK_INTP_GETSOCKOPT, -[AC_MSG_CHECKING([ifelse([$1], , [if getsockopt() needs int* parameters], -[if getsockopt() needs int* parameters (in $1)])]) -AH_TEMPLATE(HAVE_INTP_GETSOCKOPT, [Define if your system declares argument 5 of getsockopt() - as int * instead of size_t * or socklen_t.]) -ifelse([$1], , [ac_includes="" -], -[ac_includes="" -for ac_header in $1 -do - ac_includes="$ac_includes -#include<$ac_header>" -done]) -AC_CACHE_VAL(ac_cv_prototype_intp_getsockopt, -[AC_TRY_COMPILE( -[#ifdef __cplusplus -extern "C" { -#endif -$ac_includes -#ifdef __cplusplus -} -#endif -] -, -[ - int i; - size_t optlen; - i = getsockopt(0, 0, 0, 0, &optlen); -], -eval "ac_cv_prototype_intp_getsockopt=no", -[AC_TRY_COMPILE( -[#ifdef __cplusplus -extern "C" { -#endif -$ac_includes -#ifdef __cplusplus -} -#endif -] -, -[ - int i; - int optlen; - i = getsockopt(0, 0, 0, 0, &optlen); -], -eval "ac_cv_prototype_intp_getsockopt=yes", eval "ac_cv_prototype_intp_getsockopt=no")])]) -if eval "test \"`echo $ac_cv_prototype_intp_getsockopt`\" = yes"; then - AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_INTP_GETSOCKOPT) - ifelse([$2], , :, [$2]) -else - AC_MSG_RESULT(no) - ifelse([$3], , , [$3]) -fi -]) - - -dnl AC_CXX_STD_NOTHROW checks if the compiler supports non-throwing new using -dnl std::nothrow. -dnl -AC_DEFUN([AC_CXX_STD_NOTHROW], -[AH_TEMPLATE(HAVE_STD__NOTHROW, [Define if the compiler supports std::nothrow.]) -AC_CACHE_CHECK(whether the compiler supports std::nothrow, -ac_cv_cxx_std_nothrow, -[AC_LANG_SAVE - AC_LANG_CPLUSPLUS - AC_TRY_COMPILE([#include ],[int *i = new (std::nothrow) int], - ac_cv_cxx_std_nothrow=yes, ac_cv_cxx_std_nothrow=no) - AC_LANG_RESTORE -]) -if test "$ac_cv_cxx_std_nothrow" = yes; then - AC_DEFINE(HAVE_STD__NOTHROW) -fi -]) - - -dnl AC_CXX_NOTHROW_DELETE checks if the compiler supports non-throwing delete using -dnl std::nothrow. -dnl -AC_DEFUN([AC_CXX_NOTHROW_DELETE], -[AH_TEMPLATE(HAVE_NOTHROW_DELETE, [Define if the compiler supports operator delete (std::nothrow).]) -AC_CACHE_CHECK(whether the compiler supports operator delete (std::nothrow), -ac_cv_cxx_nothrow_delete, -[AC_LANG_SAVE - AC_LANG_CPLUSPLUS - AC_TRY_COMPILE([#include ],[int *i = new (std::nothrow) int; operator delete (i,std::nothrow)], - ac_cv_cxx_nothrow_delete=yes, ac_cv_cxx_nothrow_delete=no) - AC_LANG_RESTORE -]) -if test "$ac_cv_cxx_nothrow_delete" = yes; then - AC_DEFINE(HAVE_NOTHROW_DELETE) -fi -]) - - dnl AC_CXX_STATIC_ASSERT checks if the compiler supports static_assert. dnl AC_DEFUN([AC_CXX_STATIC_ASSERT], diff --git a/config/configure b/config/configure index 5b46e1b2..87b1c260 100755 --- a/config/configure +++ b/config/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for dcmtk 3.6.9. +# Generated by GNU Autoconf 2.69 for dcmtk 3.7.0. # # Report bugs to . # @@ -579,9 +579,9 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='dcmtk' -PACKAGE_TARNAME='dcmtk-3.6.9' -PACKAGE_VERSION='3.6.9' -PACKAGE_STRING='dcmtk 3.6.9' +PACKAGE_TARNAME='dcmtk-3.7.0' +PACKAGE_VERSION='3.7.0' +PACKAGE_STRING='dcmtk 3.7.0' PACKAGE_BUGREPORT='bugs@dcmtk.org' PACKAGE_URL='https://www.dcmtk.org/' @@ -804,13 +804,13 @@ bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' -datadir='${datarootdir}/dcmtk-3.6.9' -sysconfdir='${prefix}/etc/dcmtk-3.6.9' +datadir='${datarootdir}/dcmtk-3.7.0' +sysconfdir='${prefix}/etc/dcmtk-3.7.0' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' -docdir='${datarootdir}/doc/dcmtk-3.6.9' +docdir='${datarootdir}/doc/dcmtk-3.7.0' infodir='${datarootdir}/info' htmldir='${docdir}/html' dvidir='${docdir}' @@ -1310,7 +1310,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures dcmtk 3.6.9 to adapt to many kinds of systems. +\`configure' configures dcmtk 3.7.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1347,18 +1347,18 @@ Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc/dcmtk-3.6.9] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc/dcmtk-3.7.0] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only arch.-independent data [DATAROOTDIR/dcmtk-3.6.9] + --datadir=DIR read-only arch.-independent data [DATAROOTDIR/dcmtk-3.7.0] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/dcmtk-3.6.9] + --docdir=DIR documentation root [DATAROOTDIR/doc/dcmtk-3.7.0] --htmldir=DIR html documentation [DOCDIR/html] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] @@ -1375,7 +1375,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of dcmtk 3.6.9:";; + short | recursive ) echo "Configuration of dcmtk 3.7.0:";; esac cat <<\_ACEOF @@ -1465,7 +1465,7 @@ Optional Packages: --without-openjpeg don't include OpenJPEG support --with-libsndfileinc=DIR location of libsndfile includes and libraries - --with-libsndfile include libsndfile support (default: auto) + --with-libsndfile include libsndfile support (default: no) --without-libsndfile don't include libsndfile support --with-libiconvinc=DIR location of libiconv includes and libraries --with-libiconv include libiconv support (default: auto) @@ -1551,7 +1551,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -dcmtk configure 3.6.9 +dcmtk configure 3.7.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2411,7 +2411,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by dcmtk $as_me 3.6.9, which was +It was created by dcmtk $as_me 3.7.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2845,9 +2845,9 @@ ac_config_headers="$ac_config_headers include/dcmtk/config/osconfig.h" -PACKAGE_VERSION_NUMBER=368 -PACKAGE_VERSION_SUFFIX="+" -PACKAGE_DATE="DEV" +PACKAGE_VERSION_NUMBER=370 +PACKAGE_VERSION_SUFFIX="" +PACKAGE_DATE="2025-12-15" cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION_NUMBER ${PACKAGE_VERSION_NUMBER} @@ -5554,18 +5554,6 @@ _ACEOF fi -ac_fn_c_check_type "$LINENO" "sigjmp_buf" "ac_cv_type_sigjmp_buf" "#include -" -if test "x$ac_cv_type_sigjmp_buf" = xyes; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_SIGJMP_BUF 1 -_ACEOF - - -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working memcmp" >&5 $as_echo_n "checking for working memcmp... " >&6; } @@ -5674,47 +5662,23 @@ _ACEOF fi done -for ac_func in itoa atoll -do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - -for ac_func in bcmp -do : - ac_fn_c_check_func "$LINENO" "bcmp" "ac_cv_func_bcmp" -if test "x$ac_cv_func_bcmp" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_BCMP 1 -_ACEOF - -fi -done - -for ac_func in getpid mktemp mkstemp +for ac_func in atoll do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + ac_fn_c_check_func "$LINENO" "atoll" "ac_cv_func_atoll" +if test "x$ac_cv_func_atoll" = xyes; then : cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +#define HAVE_ATOLL 1 _ACEOF fi done -for ac_func in stat +for ac_func in mkstemp do : - ac_fn_c_check_func "$LINENO" "stat" "ac_cv_func_stat" -if test "x$ac_cv_func_stat" = xyes; then : + ac_fn_c_check_func "$LINENO" "mkstemp" "ac_cv_func_mkstemp" +if test "x$ac_cv_func_mkstemp" = xyes; then : cat >>confdefs.h <<_ACEOF -#define HAVE_STAT 1 +#define HAVE_MKSTEMP 1 _ACEOF fi @@ -5731,18 +5695,6 @@ _ACEOF fi done -for ac_func in strdup index rindex access -do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - for ac_func in uname cuserid getlogin getlogin_r do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` @@ -5778,17 +5730,6 @@ _ACEOF fi done -for ac_func in listen -do : - ac_fn_c_check_func "$LINENO" "listen" "ac_cv_func_listen" -if test "x$ac_cv_func_listen" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LISTEN 1 -_ACEOF - -fi -done - for ac_func in gethostbyname_r do : ac_fn_c_check_func "$LINENO" "gethostbyname_r" "ac_cv_func_gethostbyname_r" @@ -5812,17 +5753,6 @@ _ACEOF fi done -for ac_func in getrusage -do : - ac_fn_c_check_func "$LINENO" "getrusage" "ac_cv_func_getrusage" -if test "x$ac_cv_func_getrusage" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_GETRUSAGE 1 -_ACEOF - -fi -done - for ac_func in gettimeofday do : ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday" @@ -5892,17 +5822,6 @@ _ACEOF fi done -for ac_func in vsnprintf -do : - ac_fn_c_check_func "$LINENO" "vsnprintf" "ac_cv_func_vsnprintf" -if test "x$ac_cv_func_vsnprintf" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_VSNPRINTF 1 -_ACEOF - -fi -done - for ac_func in popen pclose do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` @@ -5996,8 +5915,6 @@ fi - - for ac_func in ftime gmtime_r localtime_r lstat nanosleep fcntl do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` @@ -6048,41 +5965,6 @@ done -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for socklen_t" >&5 -$as_echo_n "checking for socklen_t... " >&6; } -if ${ac_cv_type_socklen_t+:} false; then : - $as_echo_n "(cached) " >&6 -else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - #include -int -main () -{ -socklen_t len = 42; return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_type_socklen_t=yes -else - ac_cv_type_socklen_t=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_socklen_t" >&5 -$as_echo "$ac_cv_type_socklen_t" >&6; } - if test $ac_cv_type_socklen_t != yes; then - $as_echo "#define socklen_t int" >>confdefs.h - - fi - - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -6223,22 +6105,6 @@ fi -SAVELIBS="$LIBS" -LIBS="$LIBS -lm" -for ac_func in finite -do : - ac_fn_c_check_func "$LINENO" "finite" "ac_cv_func_finite" -if test "x$ac_cv_func_finite" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_FINITE 1 -_ACEOF - -fi -done - -LIBS="$SAVELIBS" - - ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -6247,143 +6113,6 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports std::nothrow" >&5 -$as_echo_n "checking whether the compiler supports std::nothrow... " >&6; } -if ${ac_cv_cxx_std_nothrow+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -int *i = new (std::nothrow) int - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_cxx_std_nothrow=yes -else - ac_cv_cxx_std_nothrow=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_std_nothrow" >&5 -$as_echo "$ac_cv_cxx_std_nothrow" >&6; } -if test "$ac_cv_cxx_std_nothrow" = yes; then - $as_echo "#define HAVE_STD__NOTHROW 1" >>confdefs.h - -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports operator delete (std::nothrow)" >&5 -$as_echo_n "checking whether the compiler supports operator delete (std::nothrow)... " >&6; } -if ${ac_cv_cxx_nothrow_delete+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -int *i = new (std::nothrow) int; operator delete (i,std::nothrow) - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_cxx_nothrow_delete=yes -else - ac_cv_cxx_nothrow_delete=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_nothrow_delete" >&5 -$as_echo "$ac_cv_cxx_nothrow_delete" >&6; } -if test "$ac_cv_cxx_nothrow_delete" = yes; then - $as_echo "#define HAVE_NOTHROW_DELETE 1" >>confdefs.h - -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports static_assert" >&5 -$as_echo_n "checking whether the compiler supports static_assert... " >&6; } -if ${ac_cv_cxx_static_assert+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -static_assert(true, "good") - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_cxx_static_assert=yes -else - ac_cv_cxx_static_assert=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_static_assert" >&5 -$as_echo "$ac_cv_cxx_static_assert" >&6; } -if test "$ac_cv_cxx_static_assert" = yes; then - $as_echo "#define HAVE_STATIC_ASSERT 1" >>confdefs.h - -fi - - - DEBUG="-DNDEBUG" DEBUGCXXFLAGS= DEBUGCFLAGS= @@ -6492,6 +6221,7 @@ fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -lg++" >&5 $as_echo_n "checking for -lg++... " >&6; } if ${ac_cv_lib_gxx+:} false; then : @@ -6536,13 +6266,15 @@ $as_echo "no" >&6; } fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lnsl" >&5 -$as_echo_n "checking for main in -lnsl... " >&6; } -if ${ac_cv_lib_nsl_main+:} false; then : +OPENSSLLIBS="" +OPENJPEGLIBS="" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -ldl" >&5 +$as_echo_n "checking for main in -ldl... " >&6; } +if ${ac_cv_lib_dl_main+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS -LIBS="-lnsl $LIBS" +LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -6556,33 +6288,31 @@ return main (); } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_nsl_main=yes + ac_cv_lib_dl_main=yes else - ac_cv_lib_nsl_main=no + ac_cv_lib_dl_main=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_main" >&5 -$as_echo "$ac_cv_lib_nsl_main" >&6; } -if test "x$ac_cv_lib_nsl_main" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBNSL 1 -_ACEOF +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_main" >&5 +$as_echo "$ac_cv_lib_dl_main" >&6; } +if test "x$ac_cv_lib_dl_main" = xyes; then : - LIBS="-lnsl $LIBS" +OPENSSLLIBS="-ldl" +OPENJPEGLIBS="-ldl" fi -if test $ac_cv_lib_nsl_main = no ; then -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lnsl" >&5 -$as_echo_n "checking for gethostbyname in -lnsl... " >&6; } -if ${ac_cv_lib_nsl_gethostbyname+:} false; then : +if test $ac_cv_lib_dl_main = no ; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS -LIBS="-lnsl $LIBS" +LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -6592,43 +6322,43 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext #ifdef __cplusplus extern "C" #endif -char gethostbyname (); +char dlopen (); int main () { -return gethostbyname (); +return dlopen (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_nsl_gethostbyname=yes + ac_cv_lib_dl_dlopen=yes else - ac_cv_lib_nsl_gethostbyname=no + ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_gethostbyname" >&5 -$as_echo "$ac_cv_lib_nsl_gethostbyname" >&6; } -if test "x$ac_cv_lib_nsl_gethostbyname" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBNSL 1 -_ACEOF +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : - LIBS="-lnsl $LIBS" +OPENSSLLIBS="-ldl" +OPENJPEGLIBS="-ldl" fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lsocket" >&5 -$as_echo_n "checking for main in -lsocket... " >&6; } -if ${ac_cv_lib_socket_main+:} false; then : + +MATHLIBS="" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lm" >&5 +$as_echo_n "checking for main in -lm... " >&6; } +if ${ac_cv_lib_m_main+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS -LIBS="-lsocket $LIBS" +LIBS="-lm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -6642,33 +6372,30 @@ return main (); } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_socket_main=yes + ac_cv_lib_m_main=yes else - ac_cv_lib_socket_main=no + ac_cv_lib_m_main=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_main" >&5 -$as_echo "$ac_cv_lib_socket_main" >&6; } -if test "x$ac_cv_lib_socket_main" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBSOCKET 1 -_ACEOF +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_main" >&5 +$as_echo "$ac_cv_lib_m_main" >&6; } +if test "x$ac_cv_lib_m_main" = xyes; then : - LIBS="-lsocket $LIBS" +MATHLIBS="-lm" fi -if test $ac_cv_lib_socket_main = no ; then -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lsocket" >&5 -$as_echo_n "checking for socket in -lsocket... " >&6; } -if ${ac_cv_lib_socket_socket+:} false; then : +if test $ac_cv_lib_m_main = no ; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sin in -lm" >&5 +$as_echo_n "checking for sin in -lm... " >&6; } +if ${ac_cv_lib_m_sin+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS -LIBS="-lsocket $LIBS" +LIBS="-lm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -6678,189 +6405,19 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext #ifdef __cplusplus extern "C" #endif -char socket (); +char sin (); int main () { -return socket (); +return sin (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_socket_socket=yes + ac_cv_lib_m_sin=yes else - ac_cv_lib_socket_socket=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_socket" >&5 -$as_echo "$ac_cv_lib_socket_socket" >&6; } -if test "x$ac_cv_lib_socket_socket" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBSOCKET 1 -_ACEOF - - LIBS="-lsocket $LIBS" - -fi - -fi - -OPENSSLLIBS="" -OPENJPEGLIBS="" -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -ldl" >&5 -$as_echo_n "checking for main in -ldl... " >&6; } -if ${ac_cv_lib_dl_main+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldl $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - -int -main () -{ -return main (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_dl_main=yes -else - ac_cv_lib_dl_main=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_main" >&5 -$as_echo "$ac_cv_lib_dl_main" >&6; } -if test "x$ac_cv_lib_dl_main" = xyes; then : - -OPENSSLLIBS="-ldl" -OPENJPEGLIBS="-ldl" - -fi - -if test $ac_cv_lib_dl_main = no ; then -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 -$as_echo_n "checking for dlopen in -ldl... " >&6; } -if ${ac_cv_lib_dl_dlopen+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldl $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (); -int -main () -{ -return dlopen (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_dl_dlopen=yes -else - ac_cv_lib_dl_dlopen=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 -$as_echo "$ac_cv_lib_dl_dlopen" >&6; } -if test "x$ac_cv_lib_dl_dlopen" = xyes; then : - -OPENSSLLIBS="-ldl" -OPENJPEGLIBS="-ldl" - -fi - -fi - -MATHLIBS="" -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lm" >&5 -$as_echo_n "checking for main in -lm... " >&6; } -if ${ac_cv_lib_m_main+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lm $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - -int -main () -{ -return main (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_m_main=yes -else - ac_cv_lib_m_main=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_main" >&5 -$as_echo "$ac_cv_lib_m_main" >&6; } -if test "x$ac_cv_lib_m_main" = xyes; then : - -MATHLIBS="-lm" - -fi - -if test $ac_cv_lib_m_main = no ; then -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sin in -lm" >&5 -$as_echo_n "checking for sin in -lm... " >&6; } -if ${ac_cv_lib_m_sin+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lm $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char sin (); -int -main () -{ -return sin (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_m_sin=yes -else - ac_cv_lib_m_sin=no + ac_cv_lib_m_sin=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext @@ -7375,18 +6932,6 @@ fi done -for ac_header in fcntl.h -do : - ac_fn_cxx_check_header_mongrel "$LINENO" "fcntl.h" "ac_cv_header_fcntl_h" "$ac_includes_default" -if test "x$ac_cv_header_fcntl_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_FCNTL_H 1 -_ACEOF - -fi - -done - for ac_header in fnmatch.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "fnmatch.h" "ac_cv_header_fnmatch_h" "$ac_includes_default" @@ -7459,18 +7004,6 @@ fi done -for ac_header in malloc.h -do : - ac_fn_cxx_check_header_mongrel "$LINENO" "malloc.h" "ac_cv_header_malloc_h" "$ac_includes_default" -if test "x$ac_cv_header_malloc_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_MALLOC_H 1 -_ACEOF - -fi - -done - for ac_header in mqueue.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "mqueue.h" "ac_cv_header_mqueue_h" "$ac_includes_default" @@ -7555,18 +7088,6 @@ fi done -for ac_header in stdint.h -do : - ac_fn_cxx_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default" -if test "x$ac_cv_header_stdint_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_STDINT_H 1 -_ACEOF - -fi - -done - for ac_header in strings.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "strings.h" "ac_cv_header_strings_h" "$ac_includes_default" @@ -7591,18 +7112,6 @@ fi done -for ac_header in sys/errno.h -do : - ac_fn_cxx_check_header_mongrel "$LINENO" "sys/errno.h" "ac_cv_header_sys_errno_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_errno_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_SYS_ERRNO_H 1 -_ACEOF - -fi - -done - for ac_header in sys/file.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "sys/file.h" "ac_cv_header_sys_file_h" "$ac_includes_default" @@ -7675,18 +7184,6 @@ fi done -for ac_header in sys/stat.h -do : - ac_fn_cxx_check_header_mongrel "$LINENO" "sys/stat.h" "ac_cv_header_sys_stat_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_stat_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_SYS_STAT_H 1 -_ACEOF - -fi - -done - for ac_header in sys/syscall.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "sys/syscall.h" "ac_cv_header_sys_syscall_h" "$ac_includes_default" @@ -7735,18 +7232,6 @@ fi done -for ac_header in sys/types.h -do : - ac_fn_cxx_check_header_mongrel "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_types_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_SYS_TYPES_H 1 -_ACEOF - -fi - -done - for ac_header in sys/un.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "sys/un.h" "ac_cv_header_sys_un_h" "$ac_includes_default" @@ -8787,39 +8272,6 @@ if test $ac_cv_c_char_unsigned = yes && test "$GCC" != yes; then fi -# The cast to long int works around a bug in the HP C Compiler -# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. -# This bug is HP SR number 8606223364. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of short" >&5 -$as_echo_n "checking size of short... " >&6; } -if ${ac_cv_sizeof_short+:} false; then : - $as_echo_n "(cached) " >&6 -else - if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (short))" "ac_cv_sizeof_short" "$ac_includes_default"; then : - -else - if test "$ac_cv_type_short" = yes; then - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (short) -See \`config.log' for more details" "$LINENO" 5; } - else - ac_cv_sizeof_short=0 - fi -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_short" >&5 -$as_echo "$ac_cv_sizeof_short" >&6; } - - - -cat >>confdefs.h <<_ACEOF -#define SIZEOF_SHORT $ac_cv_sizeof_short -_ACEOF - - # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. @@ -8890,104 +8342,38 @@ _ACEOF # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of float" >&5 -$as_echo_n "checking size of float... " >&6; } -if ${ac_cv_sizeof_float+:} false; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5 +$as_echo_n "checking size of void *... " >&6; } +if ${ac_cv_sizeof_void_p+:} false; then : $as_echo_n "(cached) " >&6 else - if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (float))" "ac_cv_sizeof_float" "$ac_includes_default"; then : + if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p" "$ac_includes_default"; then : else - if test "$ac_cv_type_float" = yes; then + if test "$ac_cv_type_void_p" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (float) +as_fn_error 77 "cannot compute sizeof (void *) See \`config.log' for more details" "$LINENO" 5; } else - ac_cv_sizeof_float=0 + ac_cv_sizeof_void_p=0 fi fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_float" >&5 -$as_echo "$ac_cv_sizeof_float" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5 +$as_echo "$ac_cv_sizeof_void_p" >&6; } cat >>confdefs.h <<_ACEOF -#define SIZEOF_FLOAT $ac_cv_sizeof_float +#define SIZEOF_VOID_P $ac_cv_sizeof_void_p _ACEOF -# The cast to long int works around a bug in the HP C Compiler -# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. -# This bug is HP SR number 8606223364. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of double" >&5 -$as_echo_n "checking size of double... " >&6; } -if ${ac_cv_sizeof_double+:} false; then : - $as_echo_n "(cached) " >&6 -else - if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (double))" "ac_cv_sizeof_double" "$ac_includes_default"; then : - -else - if test "$ac_cv_type_double" = yes; then - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (double) -See \`config.log' for more details" "$LINENO" 5; } - else - ac_cv_sizeof_double=0 - fi -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_double" >&5 -$as_echo "$ac_cv_sizeof_double" >&6; } - - - -cat >>confdefs.h <<_ACEOF -#define SIZEOF_DOUBLE $ac_cv_sizeof_double -_ACEOF - - -# The cast to long int works around a bug in the HP C Compiler -# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. -# This bug is HP SR number 8606223364. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5 -$as_echo_n "checking size of void *... " >&6; } -if ${ac_cv_sizeof_void_p+:} false; then : - $as_echo_n "(cached) " >&6 -else - if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p" "$ac_includes_default"; then : - -else - if test "$ac_cv_type_void_p" = yes; then - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (void *) -See \`config.log' for more details" "$LINENO" 5; } - else - ac_cv_sizeof_void_p=0 - fi -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5 -$as_echo "$ac_cv_sizeof_void_p" >&6; } - - - -cat >>confdefs.h <<_ACEOF -#define SIZEOF_VOID_P $ac_cv_sizeof_void_p -_ACEOF - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 -$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } -if ${ac_cv_struct_tm+:} false; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 +$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } +if ${ac_cv_struct_tm+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -9115,65 +8501,6 @@ $as_echo "$ac_cv_have___func___macro" >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for feenableexcept (in fenv.h)" >&5 -$as_echo_n "checking prototype for feenableexcept (in fenv.h)... " >&6; } - -: -ac_includes="" -for ac_header in fenv.h -do - ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` - if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then - ac_includes="$ac_includes -#include<$ac_header>" - fi -done -tmp_save_1=`echo feenableexcept | tr ' :' '__'` -if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __cplusplus -extern "C" { -#endif -$ac_includes -#ifdef __cplusplus -} -#endif -typedef union { int member; } dummyStruct; -#ifdef __cplusplus -extern "C" -#endif -dummyStruct feenableexcept(dummyStruct); - - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_prototype_$tmp_save_1=no" -else - eval "ac_cv_prototype_$tmp_save_1=yes" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - $as_echo "#define HAVE_PROTOTYPE_FEENABLEEXCEPT 1" >>confdefs.h - - : -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for _stricmp (in string.h)" >&5 $as_echo_n "checking prototype for _stricmp (in string.h)... " >&6; } @@ -9233,162 +8560,12 @@ else $as_echo "no" >&6; } fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if accept() needs int* parameters (in sys/types.h sys/socket.h)" >&5 -$as_echo_n "checking if accept() needs int* parameters (in sys/types.h sys/socket.h)... " >&6; } - -ac_includes="" -for ac_header in sys/types.h sys/socket.h -do - ac_includes="$ac_includes -#include<$ac_header>" -done -if ${ac_cv_prototype_intp_accept+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __cplusplus -extern "C" { -#endif -$ac_includes -#ifdef __cplusplus -} -#endif - - -int -main () -{ - - int i; - struct sockaddr *addr; - size_t addrlen; - - addr = 0; - addrlen = 0; - i = accept(1, addr, &addrlen); - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_prototype_intp_accept=no" -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __cplusplus -extern "C" { -#endif -$ac_includes -#ifdef __cplusplus -} -#endif - - -int -main () -{ - - int i; - struct sockaddr *addr; - int addrlen; - - addr = 0; - addrlen = 0; - i = accept(1, addr, &addrlen); - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_prototype_intp_accept=yes" -else - eval "ac_cv_prototype_intp_accept=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi - -if eval "test \"`echo $ac_cv_prototype_intp_accept`\" = yes"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - $as_echo "#define HAVE_INTP_ACCEPT 1" >>confdefs.h - - : -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for finite (in math.h)" >&5 -$as_echo_n "checking prototype for finite (in math.h)... " >&6; } - -: -ac_includes="" -for ac_header in math.h -do - ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` - if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then - ac_includes="$ac_includes -#include<$ac_header>" - fi -done -tmp_save_1=`echo finite | tr ' :' '__'` -if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __cplusplus -extern "C" { -#endif -$ac_includes -#ifdef __cplusplus -} -#endif -typedef union { int member; } dummyStruct; -#ifdef __cplusplus -extern "C" -#endif -dummyStruct finite(dummyStruct); - - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_prototype_$tmp_save_1=no" -else - eval "ac_cv_prototype_$tmp_save_1=yes" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - $as_echo "#define HAVE_PROTOTYPE_FINITE 1" >>confdefs.h - - : -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for std::isinf (in cmath)" >&5 -$as_echo_n "checking prototype for std::isinf (in cmath)... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for flock (in sys/file.h)" >&5 +$as_echo_n "checking prototype for flock (in sys/file.h)... " >&6; } : ac_includes="" -for ac_header in cmath +for ac_header in sys/file.h do ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then @@ -9396,7 +8573,7 @@ do #include<$ac_header>" fi done -tmp_save_1=`echo std::isinf | tr ' :' '__'` +tmp_save_1=`echo flock | tr ' :' '__'` if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : $as_echo_n "(cached) " >&6 else @@ -9413,7 +8590,7 @@ typedef union { int member; } dummyStruct; #ifdef __cplusplus extern "C" #endif -dummyStruct std::isinf(dummyStruct); +dummyStruct flock(dummyStruct); int @@ -9434,7 +8611,7 @@ fi if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - $as_echo "#define HAVE_PROTOTYPE_STD__ISINF 1" >>confdefs.h + $as_echo "#define HAVE_PROTOTYPE_FLOCK 1" >>confdefs.h : else @@ -9442,307 +8619,12 @@ else $as_echo "no" >&6; } fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for std::isnan (in cmath)" >&5 -$as_echo_n "checking prototype for std::isnan (in cmath)... " >&6; } - -: -ac_includes="" -for ac_header in cmath -do - ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` - if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then - ac_includes="$ac_includes -#include<$ac_header>" - fi -done -tmp_save_1=`echo std::isnan | tr ' :' '__'` -if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __cplusplus -extern "C" { -#endif -$ac_includes -#ifdef __cplusplus -} -#endif -typedef union { int member; } dummyStruct; -#ifdef __cplusplus -extern "C" -#endif -dummyStruct std::isnan(dummyStruct); - - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_prototype_$tmp_save_1=no" -else - eval "ac_cv_prototype_$tmp_save_1=yes" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - $as_echo "#define HAVE_PROTOTYPE_STD__ISNAN 1" >>confdefs.h - - : -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for flock (in sys/file.h)" >&5 -$as_echo_n "checking prototype for flock (in sys/file.h)... " >&6; } - -: -ac_includes="" -for ac_header in sys/file.h -do - ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` - if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then - ac_includes="$ac_includes -#include<$ac_header>" - fi -done -tmp_save_1=`echo flock | tr ' :' '__'` -if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __cplusplus -extern "C" { -#endif -$ac_includes -#ifdef __cplusplus -} -#endif -typedef union { int member; } dummyStruct; -#ifdef __cplusplus -extern "C" -#endif -dummyStruct flock(dummyStruct); - - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_prototype_$tmp_save_1=no" -else - eval "ac_cv_prototype_$tmp_save_1=yes" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - $as_echo "#define HAVE_PROTOTYPE_FLOCK 1" >>confdefs.h - - : -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for gethostbyname_r (in libc.h unistd.h stdlib.h netdb.h)" >&5 -$as_echo_n "checking prototype for gethostbyname_r (in libc.h unistd.h stdlib.h netdb.h)... " >&6; } - -: -ac_includes="" -for ac_header in libc.h unistd.h stdlib.h netdb.h -do - ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` - if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then - ac_includes="$ac_includes -#include<$ac_header>" - fi -done -tmp_save_1=`echo gethostbyname_r | tr ' :' '__'` -if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __cplusplus -extern "C" { -#endif -$ac_includes -#ifdef __cplusplus -} -#endif -typedef union { int member; } dummyStruct; -#ifdef __cplusplus -extern "C" -#endif -dummyStruct gethostbyname_r(dummyStruct); - - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_prototype_$tmp_save_1=no" -else - eval "ac_cv_prototype_$tmp_save_1=yes" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - $as_echo "#define HAVE_PROTOTYPE_GETHOSTBYNAME_R 1" >>confdefs.h - - : -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for gethostbyaddr_r (in libc.h unistd.h stdlib.h netdb.h)" >&5 -$as_echo_n "checking prototype for gethostbyaddr_r (in libc.h unistd.h stdlib.h netdb.h)... " >&6; } - -: -ac_includes="" -for ac_header in libc.h unistd.h stdlib.h netdb.h -do - ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` - if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then - ac_includes="$ac_includes -#include<$ac_header>" - fi -done -tmp_save_1=`echo gethostbyaddr_r | tr ' :' '__'` -if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __cplusplus -extern "C" { -#endif -$ac_includes -#ifdef __cplusplus -} -#endif -typedef union { int member; } dummyStruct; -#ifdef __cplusplus -extern "C" -#endif -dummyStruct gethostbyaddr_r(dummyStruct); - - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_prototype_$tmp_save_1=no" -else - eval "ac_cv_prototype_$tmp_save_1=yes" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - $as_echo "#define HAVE_PROTOTYPE_GETHOSTBYADDR_R 1" >>confdefs.h - - : -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for gethostid (in libc.h unistd.h stdlib.h netdb.h)" >&5 -$as_echo_n "checking prototype for gethostid (in libc.h unistd.h stdlib.h netdb.h)... " >&6; } - -: -ac_includes="" -for ac_header in libc.h unistd.h stdlib.h netdb.h -do - ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` - if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then - ac_includes="$ac_includes -#include<$ac_header>" - fi -done -tmp_save_1=`echo gethostid | tr ' :' '__'` -if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __cplusplus -extern "C" { -#endif -$ac_includes -#ifdef __cplusplus -} -#endif -typedef union { int member; } dummyStruct; -#ifdef __cplusplus -extern "C" -#endif -dummyStruct gethostid(dummyStruct); - - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_prototype_$tmp_save_1=no" -else - eval "ac_cv_prototype_$tmp_save_1=yes" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - $as_echo "#define HAVE_PROTOTYPE_GETHOSTID 1" >>confdefs.h - - : -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for strerror_r (in string.h)" >&5 -$as_echo_n "checking prototype for strerror_r (in string.h)... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for gethostbyname_r (in libc.h unistd.h stdlib.h netdb.h)" >&5 +$as_echo_n "checking prototype for gethostbyname_r (in libc.h unistd.h stdlib.h netdb.h)... " >&6; } : ac_includes="" -for ac_header in string.h +for ac_header in libc.h unistd.h stdlib.h netdb.h do ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then @@ -9750,7 +8632,7 @@ do #include<$ac_header>" fi done -tmp_save_1=`echo strerror_r | tr ' :' '__'` +tmp_save_1=`echo gethostbyname_r | tr ' :' '__'` if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : $as_echo_n "(cached) " >&6 else @@ -9767,7 +8649,7 @@ typedef union { int member; } dummyStruct; #ifdef __cplusplus extern "C" #endif -dummyStruct strerror_r(dummyStruct); +dummyStruct gethostbyname_r(dummyStruct); int @@ -9788,7 +8670,7 @@ fi if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - $as_echo "#define HAVE_PROTOTYPE_STRERROR_R 1" >>confdefs.h + $as_echo "#define HAVE_PROTOTYPE_GETHOSTBYNAME_R 1" >>confdefs.h : else @@ -9796,17 +8678,21 @@ else $as_echo "no" >&6; } fi -if test $ac_cv_prototype_strerror_r = yes ; then -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if strerror_r() returns a char * (in string.h)" >&5 -$as_echo_n "checking if strerror_r() returns a char * (in string.h)... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for gethostbyaddr_r (in libc.h unistd.h stdlib.h netdb.h)" >&5 +$as_echo_n "checking prototype for gethostbyaddr_r (in libc.h unistd.h stdlib.h netdb.h)... " >&6; } +: ac_includes="" -for ac_header in string.h +for ac_header in libc.h unistd.h stdlib.h netdb.h do - ac_includes="$ac_includes + ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` + if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then + ac_includes="$ac_includes #include<$ac_header>" + fi done -if ${ac_cv_prototype_charp_strerror_r+:} false; then : +tmp_save_1=`echo gethostbyaddr_r | tr ' :' '__'` +if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -9818,51 +8704,54 @@ $ac_includes #ifdef __cplusplus } #endif +typedef union { int member; } dummyStruct; +#ifdef __cplusplus +extern "C" +#endif +dummyStruct gethostbyaddr_r(dummyStruct); int main () { - char *buf = 0; - int i = strerror_r(0, buf, 100) - ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_prototype_charp_strerror_r=no" + eval "ac_cv_prototype_$tmp_save_1=no" else - eval "ac_cv_prototype_charp_strerror_r=yes" - + eval "ac_cv_prototype_$tmp_save_1=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi - -if eval "test \"`echo $ac_cv_prototype_charp_strerror_r`\" = yes"; then +if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - $as_echo "#define HAVE_CHARP_STRERROR_R 1" >>confdefs.h + $as_echo "#define HAVE_PROTOTYPE_GETHOSTBYADDR_R 1" >>confdefs.h : else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if getsockopt() needs int* parameters (in sys/types.h sys/socket.h)" >&5 -$as_echo_n "checking if getsockopt() needs int* parameters (in sys/types.h sys/socket.h)... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for gethostid (in libc.h unistd.h stdlib.h netdb.h)" >&5 +$as_echo_n "checking prototype for gethostid (in libc.h unistd.h stdlib.h netdb.h)... " >&6; } +: ac_includes="" -for ac_header in sys/types.h sys/socket.h +for ac_header in libc.h unistd.h stdlib.h netdb.h do - ac_includes="$ac_includes + ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` + if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then + ac_includes="$ac_includes #include<$ac_header>" + fi done -if ${ac_cv_prototype_intp_getsockopt+:} false; then : +tmp_save_1=`echo gethostid | tr ' :' '__'` +if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -9874,74 +8763,45 @@ $ac_includes #ifdef __cplusplus } #endif - - -int -main () -{ - - int i; - size_t optlen; - i = getsockopt(0, 0, 0, 0, &optlen); - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_prototype_intp_getsockopt=no" -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __cplusplus -extern "C" { -#endif -$ac_includes +typedef union { int member; } dummyStruct; #ifdef __cplusplus -} +extern "C" #endif +dummyStruct gethostid(dummyStruct); int main () { - int i; - int optlen; - i = getsockopt(0, 0, 0, 0, &optlen); - ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_prototype_intp_getsockopt=yes" + eval "ac_cv_prototype_$tmp_save_1=no" else - eval "ac_cv_prototype_intp_getsockopt=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + eval "ac_cv_prototype_$tmp_save_1=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi - -if eval "test \"`echo $ac_cv_prototype_intp_getsockopt`\" = yes"; then +if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - $as_echo "#define HAVE_INTP_GETSOCKOPT 1" >>confdefs.h + $as_echo "#define HAVE_PROTOTYPE_GETHOSTID 1" >>confdefs.h : else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for gettimeofday (in sys/time.h unistd.h)" >&5 -$as_echo_n "checking prototype for gettimeofday (in sys/time.h unistd.h)... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for strerror_r (in string.h)" >&5 +$as_echo_n "checking prototype for strerror_r (in string.h)... " >&6; } : ac_includes="" -for ac_header in sys/time.h unistd.h +for ac_header in string.h do ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then @@ -9949,7 +8809,7 @@ do #include<$ac_header>" fi done -tmp_save_1=`echo gettimeofday | tr ' :' '__'` +tmp_save_1=`echo strerror_r | tr ' :' '__'` if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : $as_echo_n "(cached) " >&6 else @@ -9966,7 +8826,7 @@ typedef union { int member; } dummyStruct; #ifdef __cplusplus extern "C" #endif -dummyStruct gettimeofday(dummyStruct); +dummyStruct strerror_r(dummyStruct); int @@ -9987,7 +8847,7 @@ fi if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - $as_echo "#define HAVE_PROTOTYPE_GETTIMEOFDAY 1" >>confdefs.h + $as_echo "#define HAVE_PROTOTYPE_STRERROR_R 1" >>confdefs.h : else @@ -9995,21 +8855,17 @@ else $as_echo "no" >&6; } fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for mktemp (in libc.h unistd.h stdlib.h)" >&5 -$as_echo_n "checking prototype for mktemp (in libc.h unistd.h stdlib.h)... " >&6; } +if test $ac_cv_prototype_strerror_r = yes ; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if strerror_r() returns a char * (in string.h)" >&5 +$as_echo_n "checking if strerror_r() returns a char * (in string.h)... " >&6; } -: ac_includes="" -for ac_header in libc.h unistd.h stdlib.h +for ac_header in string.h do - ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` - if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then - ac_includes="$ac_includes + ac_includes="$ac_includes #include<$ac_header>" - fi done -tmp_save_1=`echo mktemp | tr ' :' '__'` -if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : +if ${ac_cv_prototype_charp_strerror_r+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -10021,45 +8877,47 @@ $ac_includes #ifdef __cplusplus } #endif -typedef union { int member; } dummyStruct; -#ifdef __cplusplus -extern "C" -#endif -dummyStruct mktemp(dummyStruct); int main () { + char *buf = 0; + int i = strerror_r(0, buf, 100) + ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_prototype_$tmp_save_1=no" + eval "ac_cv_prototype_charp_strerror_r=no" else - eval "ac_cv_prototype_$tmp_save_1=yes" + eval "ac_cv_prototype_charp_strerror_r=yes" + fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi -if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then + +if eval "test \"`echo $ac_cv_prototype_charp_strerror_r`\" = yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - $as_echo "#define HAVE_PROTOTYPE_MKTEMP 1" >>confdefs.h + $as_echo "#define HAVE_CHARP_STRERROR_R 1" >>confdefs.h : else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } + fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for mkstemp (in libc.h unistd.h stdlib.h)" >&5 -$as_echo_n "checking prototype for mkstemp (in libc.h unistd.h stdlib.h)... " >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking prototype for gettimeofday (in sys/time.h unistd.h)" >&5 +$as_echo_n "checking prototype for gettimeofday (in sys/time.h unistd.h)... " >&6; } : ac_includes="" -for ac_header in libc.h unistd.h stdlib.h +for ac_header in sys/time.h unistd.h do ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then @@ -10067,7 +8925,7 @@ do #include<$ac_header>" fi done -tmp_save_1=`echo mkstemp | tr ' :' '__'` +tmp_save_1=`echo gettimeofday | tr ' :' '__'` if eval \${ac_cv_prototype_$tmp_save_1+:} false; then : $as_echo_n "(cached) " >&6 else @@ -10084,7 +8942,7 @@ typedef union { int member; } dummyStruct; #ifdef __cplusplus extern "C" #endif -dummyStruct mkstemp(dummyStruct); +dummyStruct gettimeofday(dummyStruct); int @@ -10105,7 +8963,7 @@ fi if eval "test \"`echo '$''{'ac_cv_prototype_$tmp_save_1'}'`\" = yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - $as_echo "#define HAVE_PROTOTYPE_MKSTEMP 1" >>confdefs.h + $as_echo "#define HAVE_PROTOTYPE_GETTIMEOFDAY 1" >>confdefs.h : else @@ -10410,106 +9268,6 @@ fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ standard namespace" >&5 -$as_echo_n "checking for C++ standard namespace... " >&6; } - -if ${ac_cv_check_std_namespace+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_link_o='${CXX-g++} -o conftest $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.o $LIBS 1>&5' -cat > conftest.$ac_ext < -using namespace std; - -int main() { - - cout << "Hello World" << endl; - -; return 0; } -EOF -if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link_o\""; } >&5 - (eval $ac_link_o) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - rm -rf conftest* - eval "ac_cv_check_std_namespace=yes" - else - echo "configure: failed link was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_check_std_namespace=no" - fi -else - echo "configure: failed compile was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_check_std_namespace=no" -fi -rm -f conftest* -fi -if eval "test \"`echo '$ac_cv_check_std_namespace'`\" = yes"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - $as_echo "#define HAVE_STD_NAMESPACE 1" >>confdefs.h - -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::vfprintf (in cstdarg cstdio)" >&5 -$as_echo_n "checking for std::vfprintf (in cstdarg cstdio)... " >&6; } - -ac_includes="" -for ac_header in cstdarg cstdio -do - ac_safe=`echo "$ac_header" | sed 'y%./+-%__p_%'` - if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'}'`\" = yes"; then - ac_includes="$ac_includes -#include<$ac_header>" - fi -done -tmp_save_1=`echo std::vfprintf | tr ' :' '__'` -if eval \${ac_cv_compiles_$tmp_save_1+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes -int -main () -{ -FILE *stream; va_list ap; std::vfprintf(stream, "", ap); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "ac_cv_compiles_$tmp_save_1=yes" -else - eval "ac_cv_compiles_$tmp_save_1=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -if eval "test \"`echo '$''{'ac_cv_compiles_$tmp_save_1'}'`\" = yes"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - $as_echo "#define HAVE_PROTOTYPE_STD__VFPRINTF 1" >>confdefs.h - - : -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::vsnprintf (in cstdarg cstdio)" >&5 $as_echo_n "checking for std::vsnprintf (in cstdarg cstdio)... " >&6; } @@ -11965,10 +10723,8 @@ else #endif /* on some platforms, tcpd.h needs stdio.h */ #include - #ifdef HAVE_SYS_TYPES_H /* on some platforms, tcpd.h needs sys/types.h */ #include - #endif #include #ifdef __cplusplus } @@ -12123,21 +10879,9 @@ $as_echo_n "checking whether to include libsndfile support... " >&6; } if test "${with_libsndfile+set}" = set; then : withval=$with_libsndfile; case "$withval" in yes) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - $as_echo "#define WITH_SNDFILE 1" >>confdefs.h - - SNDFILELIBS="-lsndfile" - ;; - *) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - ;; - esac -else - SAVELIBS="$LIBS" - LIBS="$LIBS -lsndfile" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + SAVELIBS="$LIBS" + LIBS="$LIBS -lsndfile" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -12151,16 +10895,25 @@ _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - $as_echo "#define WITH_SNDFILE 1" >>confdefs.h + $as_echo "#define WITH_SNDFILE 1" >>confdefs.h - SNDFILELIBS="-lsndfile" + SNDFILELIBS="-lsndfile" else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext - LIBS="$SAVELIBS" + LIBS="$SAVELIBS" + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ;; + esac +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi @@ -12168,6 +10921,7 @@ fi + # Check whether --with-libiconvinc was given. if test "${with_libiconvinc+set}" = set; then : withval=$with_libiconvinc; case $withval in #( @@ -13808,39 +12562,6 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether iterator category contiguous is declared" >&5 -$as_echo_n "checking whether iterator category contiguous is declared... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - - #include - int main(){typedef std::contiguous_iterator_tag category;return 0;} - - -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - dcmtk_have_iter_cat=yes -else - dcmtk_have_iter_cat=no - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - if test "$dcmtk_have_iter_cat" = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - -$as_echo "#define HAVE_CONTIGUOUS_ITERATOR_CATEGORY 1" >>confdefs.h - - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - fi - - - - CFLAGS="$DEBUGCFLAGS $CFLAGS" CXXFLAGS="$DEBUGCXXFLAGS $CXXFLAGS" @@ -14367,7 +13088,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by dcmtk $as_me 3.6.9, which was +This file was extended by dcmtk $as_me 3.7.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -14430,7 +13151,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -dcmtk config.status 3.6.9 +dcmtk config.status 3.7.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/config/configure.in b/config/configure.in index 4b11cc5b..87c7ce0a 100644 --- a/config/configure.in +++ b/config/configure.in @@ -1,5 +1,5 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT(dcmtk, 3.6.9, [bugs@dcmtk.org], [dcmtk-3.6.9], [https://www.dcmtk.org/]) +AC_INIT(dcmtk, 3.7.0, [bugs@dcmtk.org], [dcmtk-3.7.0], [https://www.dcmtk.org/]) AC_PREREQ(2.60) AC_CONFIG_SRCDIR(Makefile.in) AC_CONFIG_HEADERS(include/dcmtk/config/osconfig.h) @@ -10,9 +10,9 @@ dnl ------------------------------------------------------- dnl Additional Package Information dnl ------------------------------------------------------- -PACKAGE_VERSION_NUMBER=368 -PACKAGE_VERSION_SUFFIX="+" -PACKAGE_DATE="DEV" +PACKAGE_VERSION_NUMBER=370 +PACKAGE_VERSION_SUFFIX="" +PACKAGE_DATE="2025-12-15" AC_DEFINE_UNQUOTED(PACKAGE_VERSION_NUMBER,${PACKAGE_VERSION_NUMBER},[Define to the version number of this package.]) AC_DEFINE_UNQUOTED(PACKAGE_VERSION_SUFFIX,"${PACKAGE_VERSION_SUFFIX}",[Define to the version suffix of this package.]) AC_DEFINE_UNQUOTED(PACKAGE_DATE,"${PACKAGE_DATE}",[Define to the release date of this package.]) @@ -224,8 +224,6 @@ AC_CHECK_TYPES(uint64_t) AC_CHECK_TYPES(char16_t) -AC_CHECK_TYPES([sigjmp_buf], [], [], [[#include ]]) - dnl ------------------------------------------------------- dnl Check for libc library functions dnl ------------------------------------------------------- @@ -233,26 +231,20 @@ dnl ------------------------------------------------------- AC_FUNC_MEMCMP AC_TYPE_SIGNAL AC_CHECK_FUNCS(gethostid sysinfo) -AC_CHECK_FUNCS(itoa atoll) -AC_CHECK_FUNCS(bcmp) -AC_CHECK_FUNCS(getpid mktemp mkstemp) -AC_CHECK_FUNCS(stat) +AC_CHECK_FUNCS(atoll) +AC_CHECK_FUNCS(mkstemp) AC_CHECK_FUNCS(malloc_debug) -AC_CHECK_FUNCS(strdup index rindex access) AC_CHECK_FUNCS(uname cuserid getlogin getlogin_r) AC_CHECK_FUNCS(usleep) AC_CHECK_FUNCS(flock lockf) -AC_CHECK_FUNCS(listen) AC_CHECK_FUNCS(gethostbyname_r) AC_CHECK_FUNCS(gethostbyaddr_r getgrnam_r getpwnam_r) -AC_CHECK_FUNCS(getrusage) AC_CHECK_FUNCS(gettimeofday) AC_CHECK_FUNCS(waitpid) AC_CHECK_FUNCS(getuid geteuid setuid getpwnam getgrnam) AC_CHECK_FUNCS(sleep fork) AC_CHECK_FUNCS(_findfirst) AC_CHECK_FUNCS(strlcpy strlcat) -AC_CHECK_FUNCS(vsnprintf) AC_CHECK_FUNCS(popen pclose) AC_CHECK_FUNCS(readdir_r) AC_FUNC_FSEEKO @@ -261,24 +253,10 @@ dnl ------------------------------------------------------- dnl Check for header files and functions needed by oflog dnl ------------------------------------------------------- -AC_DEFUN([TYPE_SOCKLEN_T], -[ -AH_TEMPLATE(socklen_t, [Define to int if undefined.]) -AC_CACHE_CHECK([for socklen_t], ac_cv_type_socklen_t, -[ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include - #include ]], [[socklen_t len = 42; return 0;]])],[ac_cv_type_socklen_t=yes],[ac_cv_type_socklen_t=no]) -]) - if test $ac_cv_type_socklen_t != yes; then - AC_DEFINE(socklen_t, int) - fi -]) - AC_CHECK_FUNCS(ftime gmtime_r localtime_r lstat nanosleep fcntl) AC_CHECK_FUNCS(htons htonl ntohs ntohl) AC_CHECK_HEADERS(netinet/in.h) AC_CHECK_HEADERS(syslog.h) -TYPE_SOCKLEN_T AH_TEMPLATE(HAVE_ENAMETOOLONG, [Define if your system provides ENAMETOOLONG errno value.]) AC_COMPILE_IFELSE( @@ -298,25 +276,12 @@ AC_CHECK_FUNCS(fgetln) AC_CHECK_PROTOTYPE(strcasestr, string.h) -dnl ------------------------------------------------------- -dnl Check for libm library functions -dnl ------------------------------------------------------- - -SAVELIBS="$LIBS" -LIBS="$LIBS -lm" -AC_CHECK_FUNCS(finite) -LIBS="$SAVELIBS" - dnl ------------------------------------------------------- dnl Perform remaining tests with C++ compiler dnl ------------------------------------------------------- AC_LANG(C++) -AC_CXX_STD_NOTHROW -AC_CXX_NOTHROW_DELETE -AC_CXX_STATIC_ASSERT - dnl ------------------------------------------------------- dnl Check for Debug Mode dnl ------------------------------------------------------- @@ -416,18 +381,6 @@ dnl ------------------------------------------------------- AC_CHECK_GXXLIB -dnl Some C++ compilers have problems with recursive main calls -dnl (e.g. Sun C++ 4.2). In this case we must test another function -dnl to link. -AC_CHECK_LIB(nsl, main) -if test $ac_cv_lib_nsl_main = no ; then -AC_CHECK_LIB(nsl, gethostbyname) -fi -AC_CHECK_LIB(socket, main) -if test $ac_cv_lib_socket_main = no ; then -AC_CHECK_LIB(socket, socket) -fi - dnl Some newer versions of OpenSSL must be linked against libdl. dnl We just check whether libdl exists and in this case always dnl add -ldl to OPENSSLLIBS. The same applies for OPENJPEG. @@ -476,14 +429,12 @@ AC_CHECK_TCP_H AC_CHECK_HEADERS(alloca.h) AC_CHECK_HEADERS(atomic) AC_CHECK_HEADERS(arpa/inet.h) -AC_CHECK_HEADERS(fcntl.h) AC_CHECK_HEADERS(fnmatch.h) AC_CHECK_HEADERS(grp.h) AC_CHECK_HEADERS(ieeefp.h) AC_CHECK_HEADERS(io.h) AC_CHECK_HEADERS(iostream) AC_CHECK_HEADERS(libc.h) -AC_CHECK_HEADERS(malloc.h) AC_CHECK_HEADERS(mqueue.h) AC_CHECK_HEADERS(new) AC_CHECK_HEADERS(netdb.h) @@ -491,22 +442,18 @@ AC_CHECK_HEADERS(pthread.h) AC_CHECK_HEADERS(pwd.h) AC_CHECK_HEADERS(semaphore.h) AC_CHECK_HEADERS(cstddef) -AC_CHECK_HEADERS(stdint.h) AC_CHECK_HEADERS(strings.h) AC_CHECK_HEADERS(synch.h) -AC_CHECK_HEADERS(sys/errno.h) AC_CHECK_HEADERS(sys/file.h) AC_CHECK_HEADERS(sys/msg.h) AC_CHECK_HEADERS(sys/param.h) AC_CHECK_HEADERS(sys/resource.h) AC_CHECK_HEADERS(sys/select.h) AC_CHECK_HEADERS(sys/socket.h) -AC_CHECK_HEADERS(sys/stat.h) AC_CHECK_HEADERS(sys/syscall.h) AC_CHECK_HEADERS(sys/systeminfo.h) AC_CHECK_HEADERS(sys/time.h) AC_CHECK_HEADERS(sys/timeb.h) -AC_CHECK_HEADERS(sys/types.h) AC_CHECK_HEADERS(sys/un.h) AC_CHECK_HEADERS(sys/utime.h) AC_CHECK_HEADERS(sys/utsname.h) @@ -737,11 +684,8 @@ dnl ------------------------------------------------------------ AC_TYPEDEF(ssize_t, long) AC_TYPEDEF(pid_t, int) AC_C_CHAR_UNSIGNED -AC_CHECK_SIZEOF(short) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) -AC_CHECK_SIZEOF(float) -AC_CHECK_SIZEOF(double) AC_CHECK_SIZEOF(void *) AC_STRUCT_TM AC_MY_SYMBOL_EXISTS([__FUNCTION__]) @@ -756,12 +700,7 @@ dnl The following AC_CHECK_* macros _must_ have corresponding entries in dnl the acconfig.h file. This is because the macros are specific to the dnl DCMTK project and are not supported by GNU autoheader. -AC_CHECK_PROTOTYPE(feenableexcept, fenv.h) AC_CHECK_PROTOTYPE(_stricmp, string.h) -AC_CHECK_INTP_ACCEPT(sys/types.h sys/socket.h) -AC_CHECK_PROTOTYPE(finite, math.h) -AC_CHECK_PROTOTYPE(std::isinf, cmath) -AC_CHECK_PROTOTYPE(std::isnan, cmath) AC_CHECK_PROTOTYPE(flock, sys/file.h) AC_CHECK_PROTOTYPE(gethostbyname_r, libc.h unistd.h stdlib.h netdb.h) AC_CHECK_PROTOTYPE(gethostbyaddr_r, libc.h unistd.h stdlib.h netdb.h) @@ -770,23 +709,18 @@ AC_CHECK_PROTOTYPE(strerror_r, string.h) if test $ac_cv_prototype_strerror_r = yes ; then AC_CHECK_CHARP_STRERROR_R(string.h) fi -AC_CHECK_INTP_GETSOCKOPT(sys/types.h sys/socket.h) AC_CHECK_PROTOTYPE(gettimeofday, sys/time.h unistd.h) -AC_CHECK_PROTOTYPE(mktemp, libc.h unistd.h stdlib.h) -AC_CHECK_PROTOTYPE(mkstemp, libc.h unistd.h stdlib.h) AC_CHECK_PROTOTYPE(strcasecmp, string.h) AC_CHECK_PROTOTYPE(strncasecmp, string.h) AC_CHECK_PROTOTYPE(usleep, libc.h unistd.h stdlib.h) AC_CHECK_PROTOTYPE(vsnprintf, stdio.h stdarg.h) AC_CHECK_PROTOTYPE(waitpid, sys/wait.h sys/time.h sys/resource.h) + dnl ------------------------------------------------------- dnl Check for the usage of standard C++ headers dnl ------------------------------------------------------- -AC_CHECK_STD_NAMESPACE -AC_CHECK_COMPILES(std::vfprintf, cstdarg cstdio, - [FILE *stream; va_list ap; std::vfprintf(stream, "", ap);]) AC_CHECK_COMPILES(std::vsnprintf, cstdarg cstdio, [char buf[256]; va_list ap; std::vsnprintf(buf, 0, "", ap);]) @@ -1156,10 +1090,8 @@ AS_HELP_STRING([--without-libwrap], [don't include libwrap support])], #endif /* on some platforms, tcpd.h needs stdio.h */ #include - #ifdef HAVE_SYS_TYPES_H /* on some platforms, tcpd.h needs sys/types.h */ #include - #endif #include #ifdef __cplusplus } @@ -1214,28 +1146,27 @@ SNDFILELIBS="" AC_MSG_CHECKING(whether to include libsndfile support) AH_TEMPLATE(WITH_SNDFILE, [Define if we are compiling with libsndfile support.]) AC_ARG_WITH(libsndfile, - [AS_HELP_STRING([--with-libsndfile], [include libsndfile support (default: auto)]) + [AS_HELP_STRING([--with-libsndfile], [include libsndfile support (default: no)]) AS_HELP_STRING([--without-libsndfile], [don't include libsndfile support])], [ case "$withval" in yes) - AC_MSG_RESULT(yes) - AC_DEFINE(WITH_SNDFILE) - SNDFILELIBS="-lsndfile" + SAVELIBS="$LIBS" + LIBS="$LIBS -lsndfile" + AC_TRY_LINK([#include ], [char buffer [128]; sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer));], + [ AC_MSG_RESULT(yes) + AC_DEFINE(WITH_SNDFILE) + SNDFILELIBS="-lsndfile" ], + [ AC_MSG_RESULT(no) ]) + LIBS="$SAVELIBS" ;; *) AC_MSG_RESULT(no) ;; esac ], - [ SAVELIBS="$LIBS" - LIBS="$LIBS -lsndfile" - AC_TRY_LINK([#include ], [char buffer [128]; sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer));], - [ AC_MSG_RESULT(yes) - AC_DEFINE(WITH_SNDFILE) - SNDFILELIBS="-lsndfile" ], - [AC_MSG_RESULT(no)]) - LIBS="$SAVELIBS"] + [ AC_MSG_RESULT(no) ] ) + dnl ------------------------------------------------------- dnl Check for libiconv support dnl ------------------------------------------------------- @@ -1793,13 +1724,6 @@ AC_TRY_COMPILE([#include ], [AC_MSG_RESULT(no)]) -dnl ------------------------------------------------------- -dnl Test for defined iterator categories -dnl ------------------------------------------------------- - -AC_CHECK_ITERATOR_CATEGORY([contiguous],[HAVE_CONTIGUOUS_ITERATOR_CATEGORY]) - - dnl ------------------------------------------------------- dnl Set optimizer and debug compiler flags dnl ------------------------------------------------------- diff --git a/config/confmod b/config/confmod index 531d4be7..ce45475d 100755 --- a/config/confmod +++ b/config/confmod @@ -668,13 +668,13 @@ bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' -datadir='${datarootdir}/dcmtk-3.6.9' -sysconfdir='${prefix}/etc/dcmtk-3.6.9' +datadir='${datarootdir}/dcmtk-3.7.0' +sysconfdir='${prefix}/etc/dcmtk-3.7.0' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' -docdir='${datarootdir}/doc/dcmtk-3.6.9' +docdir='${datarootdir}/doc/dcmtk-3.7.0' infodir='${datarootdir}/info' htmldir='${docdir}/html' dvidir='${docdir}' @@ -1211,18 +1211,18 @@ Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc/dcmtk-3.6.9] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc/dcmtk-3.7.0] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only arch.-independent data [DATAROOTDIR/dcmtk-3.6.9] + --datadir=DIR read-only arch.-independent data [DATAROOTDIR/dcmtk-3.7.0] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/dcmtk-3.6.9] + --docdir=DIR documentation root [DATAROOTDIR/doc/dcmtk-3.7.0] --htmldir=DIR html documentation [DOCDIR/html] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] diff --git a/config/docs/macros.txt b/config/docs/macros.txt index 57fe7e43..cd9ff2f0 100644 --- a/config/docs/macros.txt +++ b/config/docs/macros.txt @@ -87,23 +87,6 @@ DCMTK_ENABLE_STRICT_HUFFMAN_TABLE_CHECK test (see DCMTK issue #1018). The test has, therefore, been disabled. This macro re-enables the old behavior. -DCMTK_ENABLE_UNSAFE_VSNPRINTF - Affected: ofstd - Type of modification: Activates feature - Explanation: DCMTK requires the snprintf(3)/vsnprintf(3) function, which was - introduced with C99 and may not be supported by very old compilers. As a - "last resort", an implementation internally using sprintf/vsprintf can be - enabled with this macro, which allows the user to compile DCMTK on platforms - that do not have the new functions. - The implementation allocates a buffer that is 1 kByte larger than the "size" - parameter, formats the string into that buffer, and then uses strlcpy() to - copy the formatted string into the output buffer, truncating if necessary. - This will work in most cases, since few snprintf calls should overrun their - buffer by more than 1K, but it can be easily abused by a malicious attacker - to cause a buffer overrun. - Therefore, this implementation should only be used as a "last resort" and we - strongly advise against using it in production code. - DCMTK_GUI Affected: all modules Type of modification: Activates experimental or rarely used feature diff --git a/config/include/dcmtk/config/osconfig.h.in b/config/include/dcmtk/config/osconfig.h.in index 76f8c71e..70cf1b52 100644 --- a/config/include/dcmtk/config/osconfig.h.in +++ b/config/include/dcmtk/config/osconfig.h.in @@ -88,9 +88,6 @@ #define ENVIRONMENT_PATH_SEPARATOR ':' #endif -/* Define to 1 if you have the `access' function. */ -#undef HAVE_ACCESS - /* Define to 1 if you have the header file. */ #undef HAVE_ALLOCA_H @@ -112,9 +109,6 @@ /* Define if the compiler supports __attribute__((deprecated("message"))). */ #undef HAVE_ATTRIBUTE_DEPRECATED_MSG -/* Define to 1 if you have the `bcmp' function. */ -#undef HAVE_BCMP - /* Define to 1 if the system has the type `char16_t'. */ #undef HAVE_CHAR16_T @@ -122,9 +116,6 @@ instead of int. */ #undef HAVE_CHARP_STRERROR_R -/* Define if the contiguous iterator category is supported. */ -#undef HAVE_CONTIGUOUS_ITERATOR_CATEGORY - /* Define to 1 if you have the header file. */ #undef HAVE_CSTDDEF @@ -166,15 +157,9 @@ /* Define to 1 if you have the `fcntl' function. */ #undef HAVE_FCNTL -/* Define to 1 if you have the header file. */ -#undef HAVE_FCNTL_H - /* Define to 1 if you have the `fgetln' function. */ #undef HAVE_FGETLN -/* Define to 1 if you have the `finite' function. */ -#undef HAVE_FINITE - /* Define to 1 if you have the `flock' function. */ #undef HAVE_FLOCK @@ -217,18 +202,12 @@ /* Define to 1 if you have the `getlogin_r' function. */ #undef HAVE_GETLOGIN_R -/* Define to 1 if you have the `getpid' function. */ -#undef HAVE_GETPID - /* Define to 1 if you have the `getpwnam' function. */ #undef HAVE_GETPWNAM /* Define to 1 if you have the `getpwnam_r' function. */ #undef HAVE_GETPWNAM_R -/* Define to 1 if you have the `getrusage' function. */ -#undef HAVE_GETRUSAGE - /* Define to 1 if you have the `gettimeofday' function. */ #undef HAVE_GETTIMEOFDAY @@ -253,20 +232,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_IEEEFP_H -/* Define to 1 if you have the `index' function. */ -#undef HAVE_INDEX - /* Define to 1 if the system has the type `int64_t'. */ #undef HAVE_INT64_T -/* Define if your system declares argument 3 of accept() as int * instead of - size_t * or socklen_t *. */ -#undef HAVE_INTP_ACCEPT - -/* Define if your system declares argument 5 of getsockopt() as int * instead - of size_t * or socklen_t. */ -#undef HAVE_INTP_GETSOCKOPT - /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H @@ -276,27 +244,15 @@ /* Define to 1 if you have the header file. */ #undef HAVE_IO_H -/* Define to 1 if you have the `itoa' function. */ -#undef HAVE_ITOA - /* Define to 1 if you have the header file. */ #undef HAVE_LANGINFO_H /* Define to 1 if you have the header file. */ #undef HAVE_LIBC_H -/* Define to 1 if you have the `nsl' library (-lnsl). */ -#undef HAVE_LIBNSL - /* Define to 1 if you have the header file. */ #undef HAVE_LIBPNG_PNG_H -/* Define to 1 if you have the `socket' library (-lsocket). */ -#undef HAVE_LIBSOCKET - -/* Define to 1 if you have the `listen' function. */ -#undef HAVE_LISTEN - /* Define to 1 if you have the `localtime_r' function. */ #undef HAVE_LOCALTIME_R @@ -312,18 +268,12 @@ /* Define to 1 if you have the `malloc_debug' function. */ #undef HAVE_MALLOC_DEBUG -/* Define to 1 if you have the header file. */ -#undef HAVE_MALLOC_H - /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `mkstemp' function. */ #undef HAVE_MKSTEMP -/* Define to 1 if you have the `mktemp' function. */ -#undef HAVE_MKTEMP - /* Define to 1 if you have the header file. */ #undef HAVE_MQUEUE_H @@ -348,9 +298,6 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NEW -/* Define if the compiler supports operator delete (std::nothrow). */ -#undef HAVE_NOTHROW_DELETE - /* Define `pid_t' to `int' if does not define. */ #undef HAVE_NO_TYPEDEF_PID_T @@ -482,12 +429,6 @@ typedef unsigned short ushort; /* Define to 1 if you have the `popen' function. */ #undef HAVE_POPEN -/* Define if your system has a prototype for feenableexcept in fenv.h. */ -#undef HAVE_PROTOTYPE_FEENABLEEXCEPT - -/* Define if your system has a prototype for finite in math.h. */ -#undef HAVE_PROTOTYPE_FINITE - /* Define if your system has a prototype for flock in sys/file.h. */ #undef HAVE_PROTOTYPE_FLOCK @@ -507,24 +448,6 @@ typedef unsigned short ushort; unistd.h. */ #undef HAVE_PROTOTYPE_GETTIMEOFDAY -/* Define if your system has a prototype for mkstemp in libc.h unistd.h - stdlib.h. */ -#undef HAVE_PROTOTYPE_MKSTEMP - -/* Define if your system has a prototype for mktemp in libc.h unistd.h - stdlib.h. */ -#undef HAVE_PROTOTYPE_MKTEMP - -/* Define if your system has a prototype for std::isinf in cmath. */ -#undef HAVE_PROTOTYPE_STD__ISINF - -/* Define if your system has a prototype for std::isnan in cmath. */ -#undef HAVE_PROTOTYPE_STD__ISNAN - -/* Define if your system has a prototype for std::vfprintf in cstdarg cstdio. - */ -#undef HAVE_PROTOTYPE_STD__VFPRINTF - /* Define if your system has a prototype for std::vsnprintf in cstdarg cstdio. */ #undef HAVE_PROTOTYPE_STD__VSNPRINTF @@ -567,39 +490,21 @@ typedef unsigned short ushort; /* Define to 1 if you have the `readdir_r' function. */ #undef HAVE_READDIR_R -/* Define to 1 if you have the `rindex' function. */ -#undef HAVE_RINDEX - /* Define to 1 if you have the header file. */ #undef HAVE_SEMAPHORE_H /* Define to 1 if you have the `setuid' function. */ #undef HAVE_SETUID -/* Define to 1 if the system has the type `sigjmp_buf'. */ -#undef HAVE_SIGJMP_BUF - /* Define to 1 if you have the `sleep' function. */ #undef HAVE_SLEEP -/* Define to 1 if you have the `stat' function. */ -#undef HAVE_STAT - -/* Define if the compiler supports static_assert. */ -#undef HAVE_STATIC_ASSERT - /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H -/* Define if ANSI standard C++ includes use std namespace. */ -#undef HAVE_STD_NAMESPACE - -/* Define if the compiler supports std::nothrow. */ -#undef HAVE_STD__NOTHROW - /* Define if STL's algorithm should be used. */ #undef HAVE_STL_ALGORITHM @@ -633,9 +538,6 @@ typedef unsigned short ushort; /* Define if STL's vector should be used. */ #undef HAVE_STL_VECTOR -/* Define to 1 if you have the `strdup' function. */ -#undef HAVE_STRDUP - /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H @@ -667,9 +569,6 @@ typedef unsigned short ushort; */ #undef HAVE_SYS_DIR_H -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_ERRNO_H - /* Define to 1 if you have the header file. */ #undef HAVE_SYS_FILE_H @@ -758,9 +657,6 @@ typedef unsigned short ushort; /* Define if variable-length arrays are supported in C. */ #undef HAVE_VLA -/* Define to 1 if you have the `vsnprintf' function. */ -#undef HAVE_VSNPRINTF - /* Define to 1 if you have the `waitpid' function. */ #undef HAVE_WAITPID @@ -812,21 +708,12 @@ typedef unsigned short ushort; /* Define as the return type of signal handlers (`int' or `void'). */ #undef RETSIGTYPE -/* The size of `double', as computed by sizeof. */ -#undef SIZEOF_DOUBLE - -/* The size of `float', as computed by sizeof. */ -#undef SIZEOF_FLOAT - /* The size of `int', as computed by sizeof. */ #undef SIZEOF_INT /* The size of `long', as computed by sizeof. */ #undef SIZEOF_LONG -/* The size of `short', as computed by sizeof. */ -#undef SIZEOF_SHORT - /* The size of `void *', as computed by sizeof. */ #undef SIZEOF_VOID_P @@ -918,9 +805,6 @@ typedef unsigned short ushort; # undef __CHAR_UNSIGNED__ #endif -/* Define to int if undefined. */ -#undef socklen_t - #if defined(HAVE_CXX11) && defined(__cplusplus) && __cplusplus < 201103L #error \ DCMTK was configured to use C++11 features, but your compiler does not or was not configured to provide them. diff --git a/config/math.cc b/config/math.cc index cccc5d50..19f19c93 100644 --- a/config/math.cc +++ b/config/math.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, OFFIS e.V. + * Copyright (C) 2015-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -46,37 +46,26 @@ struct dcmtk_config_math { static inline OFBool isnan( float f ) { -#ifdef HAVE_PROTOTYPE_STD__ISNAN return STD_NAMESPACE isnan(f); -#else - return ::isnan(f); -#endif } static inline OFBool isnan( double d ) { -#ifdef HAVE_PROTOTYPE_STD__ISNAN return STD_NAMESPACE isnan(d); -#else - return ::isnan(d); -#endif } static inline OFBool isinf( float f ) { -#ifdef HAVE_PROTOTYPE_STD__ISINF return STD_NAMESPACE isinf( f ); -#else - return ::isinf( f ); -#endif } static inline OFBool isinf( double d ) { -#ifdef HAVE_PROTOTYPE_STD__ISINF return STD_NAMESPACE isinf( d ); -#else - return ::isinf( d ); -#endif + } + + static inline double sqrt( double d ) + { + return STD_NAMESPACE sqrt(d); } }; diff --git a/config/modules b/config/modules index cd5b4344..6c9ca95f 100644 --- a/config/modules +++ b/config/modules @@ -1 +1 @@ -oficonv ofstd oflog dcmdata dcmiod dcmfg dcmseg dcmimgle dcmimage dcmjpeg dcmjpls dcmtls dcmnet dcmsr dcmsign dcmwlm dcmqrdb dcmpstat dcmrt dcmtract dcmpmap dcmect dcmapps +oficonv ofstd oflog dcmdata dcmimgle dcmimage dcmjpeg dcmjpls dcmtls dcmnet dcmsr dcmsign dcmwlm dcmqrdb dcmrt dcmiod dcmpstat dcmfg dcmseg dcmtract dcmpmap dcmect dcmapps \ No newline at end of file diff --git a/dcmapps/apps/CMakeLists.txt b/dcmapps/apps/CMakeLists.txt index 88b1ee33..249f2082 100644 --- a/dcmapps/apps/CMakeLists.txt +++ b/dcmapps/apps/CMakeLists.txt @@ -4,4 +4,4 @@ foreach(PROGRAM dcm2img) endforeach() # make sure executables are linked to the corresponding libraries -DCMTK_TARGET_LINK_MODULES(dcm2img dcmjpeg dcmjpls dcmimage dcmimgle dcmdata oflog oficonv ofstd) +DCMTK_TARGET_LINK_MODULES(dcm2img dcmjpeg dcmjpls) diff --git a/dcmapps/docs/dcm2img.man b/dcmapps/docs/dcm2img.man index aee64c3c..9acb1961 100644 --- a/dcmapps/docs/dcm2img.man +++ b/dcmapps/docs/dcm2img.man @@ -174,6 +174,22 @@ color space conversion (JPEG compressed images only): +cn --conv-never never convert color space +bits stored: + + +bs --bits-stored-fix + correct inconsistent bits stored value (default) + + # If the value of BitsStored in the compressed bitstream is smaller + # than the value in the DICOM dataset, update the value in the dataset + # (JPEG compressed images only) + + -bs --bits-stored-keep + preserve inconsistent bits stored value + + # Keep the value of BitsStored even if inconsistent with the + # compressed bitstream. This may help in correctly decoding some + # defective images JPEG compressed images only) + workaround options for incorrect encodings (JPEG compressed images only): +w6 --workaround-pred6 @@ -598,6 +614,6 @@ It is an error if no data dictionary can be loaded. \section dcm2img_copyright COPYRIGHT -Copyright (C) 2001-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2001-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmapps/include/dcmtk/dcmapps/dcm2img.h b/dcmapps/include/dcmtk/dcmapps/dcm2img.h index eae28c89..51dd9034 100644 --- a/dcmapps/include/dcmtk/dcmapps/dcm2img.h +++ b/dcmapps/include/dcmtk/dcmapps/dcm2img.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2024, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -59,9 +59,7 @@ #endif BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include /* for O_BINARY */ -#endif #ifdef HAVE_IO_H #include /* for setmode() on Windows */ #endif @@ -640,7 +638,7 @@ static E_FileType getFileTypeByExtension(const char *fname) // no filename extension, return BMP as the default for files if (pos == OFString_npos) { - OFLOG_WARN(dcm2imgLogger, "no filename extension specified, writing BMP file."); + OFLOG_WARN(dcm2imgLogger, "no filename extension specified, writing BMP file"); return EFT_BMP; } @@ -669,7 +667,7 @@ static E_FileType getFileTypeByExtension(const char *fname) #endif /* WITH_OPENJPEG */ #endif /* BUILD_DCM2IMG_AS_DCM2KIMG */ - OFLOG_WARN(dcm2imgLogger, "unsupported filename extension '" << extension << "', writing BMP file."); + OFLOG_WARN(dcm2imgLogger, "unsupported filename extension '" << extension << "', writing BMP file"); return EFT_BMP; } @@ -678,6 +676,8 @@ int main(int argc, char *argv[]) OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, consoleDescription, rcsid); OFCommandLine cmd; + int exitCode = 0; + E_FileReadMode opt_readMode = ERM_autoDetect; /* default: fileformat or dataset */ E_TransferSyntax opt_transferSyntax = EXS_Unknown; /* default: xfer syntax recognition */ @@ -737,6 +737,7 @@ int main(int argc, char *argv[]) OFBool opt_predictor6WorkaroundEnable = OFFalse; OFBool opt_cornellWorkaroundEnable = OFFalse; OFBool opt_forceSingleFragmentPerFrame = OFFalse; + OFBool opt_preserveBitsStored = OFFalse; // JPEG-LS parameters OFBool opt_true_lossless = OFFalse; @@ -767,8 +768,7 @@ int main(int argc, char *argv[]) int opt_imageInfo = 0; /* default: no info */ int opt_suppressOutput = 0; /* default: create output */ - E_FileType opt_fileType = EFT_default; - /* (binary for file output and ASCII for stdout) */ + E_FileType opt_fileType = EFT_default; /* default: auto */ OFCmdUnsignedInt opt_fileBits = 0; /* default: 0 */ const char * opt_ifname = NULL; const char * opt_ofname = NULL; @@ -846,6 +846,10 @@ int main(int argc, char *argv[]) cmd.addOption("--conv-always", "+ca", "always convert YCbCr to RGB"); cmd.addOption("--conv-never", "+cn", "never convert color space"); + cmd.addSubGroup("bits stored:"); + cmd.addOption("--bits-stored-fix", "+bs", "correct inconsistent bits stored value (default)"); + cmd.addOption("--bits-stored-keep", "-bs", "preserve inconsistent bits stored value"); + cmd.addSubGroup("workaround options for incorrect encodings (JPEG compressed images only):"); cmd.addOption("--workaround-pred6", "+w6", "enable workaround for JPEG lossless images\nwith overflow in predictor 6"); cmd.addOption("--workaround-incpl", "+wi", "enable workaround for incomplete JPEG data"); @@ -1094,9 +1098,15 @@ int main(int argc, char *argv[]) if (cmd.findOption("--check-lut-depth")) opt_compatibilityMode |= CIF_CheckLutBitDepth; if (cmd.findOption("--ignore-mlut-depth")) + { + app.checkConflict("--ignore-mlut-depth", "--check-lut-depth", (opt_compatibilityMode & CIF_CheckLutBitDepth) > 0); opt_compatibilityMode |= CIF_IgnoreModalityLutBitDepth; + } if (cmd.findOption("--ignore-vlut-depth")) + { + app.checkConflict("--ignore-vlut-depth", "--check-lut-depth", (opt_compatibilityMode & CIF_CheckLutBitDepth) > 0); opt_ignoreVoiLutDepth = OFTrue; + } /* image processing options: frame selection */ @@ -1212,6 +1222,11 @@ int main(int argc, char *argv[]) opt_decompCSconversion = EDC_never; cmd.endOptionBlock(); + cmd.beginOptionBlock(); + if (cmd.findOption("--bits-stored-fix")) opt_preserveBitsStored = OFFalse; + if (cmd.findOption("--bits-stored-keep")) opt_preserveBitsStored = OFTrue; + cmd.beginOptionBlock(); + if (cmd.findOption("--workaround-pred6")) opt_predictor6WorkaroundEnable = OFTrue; if (cmd.findOption("--workaround-incpl")) opt_forceSingleFragmentPerFrame = OFTrue; if (cmd.findOption("--workaround-cornell")) opt_cornellWorkaroundEnable = OFTrue; @@ -1592,9 +1607,7 @@ int main(int argc, char *argv[]) opt_fileType = EFT_JPLS; #ifdef BUILD_DCM2IMG_AS_DCM2KIMG if (cmd.findOption("--write-jp2k-cs")) - { opt_fileType = EFT_JP2K_CS; - } #ifdef WITH_OPENJPEG if (cmd.findOption("--write-jp2k")) { @@ -1617,8 +1630,8 @@ int main(int argc, char *argv[]) if (cmd.findOption("--write-pastel-pnm")) opt_fileType = EFT_PastelPNM; #endif - if (opt_ofname && (opt_fileType == EFT_default|| cmd.findOption("--write-auto"))) - opt_fileType = getFileTypeByExtension(opt_ofname); + if (cmd.findOption("--write-auto")) + opt_fileType = EFT_default; cmd.endOptionBlock(); } @@ -1632,8 +1645,13 @@ int main(int argc, char *argv[]) << DCM_DICT_ENVIRONMENT_VARIABLE); } - if (opt_suppressOutput && opt_ofname) - OFLOG_WARN(dcm2imgLogger, "ignoring parameter bitmap-out because of option --no-output"); + if (opt_ofname) + { + if (opt_suppressOutput) + OFLOG_WARN(dcm2imgLogger, "ignoring parameter bitmap-out because of option --no-output"); + else if (opt_fileType == EFT_default) + opt_fileType = getFileTypeByExtension(opt_ofname); + } OFLOG_INFO(dcm2imgLogger, "reading DICOM file: " << opt_ifname); @@ -1643,7 +1661,7 @@ int main(int argc, char *argv[]) // register JPEG decompression codecs DJDecoderRegistration::registerCodecs(opt_decompCSconversion, EUC_default, EPC_default, opt_predictor6WorkaroundEnable, opt_cornellWorkaroundEnable, - opt_forceSingleFragmentPerFrame); + opt_forceSingleFragmentPerFrame, opt_preserveBitsStored); // register JPEG-LS decompression codecs DJLSDecoderRegistration::registerCodecs(); @@ -1653,32 +1671,39 @@ int main(int argc, char *argv[]) if (opt_use_openjpeg) { // register global OpenJPEG decompression codecs - O2DecoderRegistration::registerCodecs(EJ2UC_default, EJ2PC_restore, OFstatic_cast(int, openJPEGNumThreads), 0 /* decoder flags */, OFTrue, OFTrue); + O2DecoderRegistration::registerCodecs(EJ2UC_default, EJ2PC_restore, OFstatic_cast(int, openJPEGNumThreads), 0 /* decoder flags */, OFTrue, OFTrue, opt_preserveBitsStored); } else { // register global JasPer decompression codecs - D2DecoderRegistration::registerCodecs(); + D2DecoderRegistration::registerCodecs(EJ2UC_default, EJ2PC_restore, OFTrue, OFTrue, opt_preserveBitsStored); } #else /* WITH_OPENJPEG */ // register global JasPer decompression codecs - D2DecoderRegistration::registerCodecs(); + D2DecoderRegistration::registerCodecs(EJ2UC_default, EJ2PC_restore, OFTrue, OFTrue, opt_preserveBitsStored); #endif /* WITH_OPENJPEG */ #endif /* BUILD_DCM2IMG_AS_DCM2KIMG */ + DcmDataset *dataset = NULL; + DicomImage *di = NULL; + DiDisplayFunction *disp = NULL; + E_TransferSyntax xfer = EXS_Unknown; + DcmFileFormat *dfile = new DcmFileFormat(); OFCondition cond = dfile->loadFile(opt_ifname, opt_transferSyntax, EGL_withoutGL, DCM_MaxReadLength, opt_readMode); if (cond.bad()) { OFLOG_FATAL(dcm2imgLogger, cond.text() << ": reading file: " << opt_ifname); - return 1; + delete dfile; + exitCode = 1; + goto cleanup; } OFLOG_INFO(dcm2imgLogger, "preparing pixel data"); - DcmDataset *dataset = dfile->getDataset(); - E_TransferSyntax xfer = dataset->getOriginalXfer(); + dataset = dfile->getDataset(); + xfer = dataset->getOriginalXfer(); Sint32 frameCount; if (dataset->findAndGetSint32(DCM_NumberOfFrames, frameCount).bad()) @@ -1694,21 +1719,22 @@ int main(int argc, char *argv[]) opt_compatibilityMode |= CIF_UsePartialAccessToPixelData; } - DicomImage *di = new DicomImage(dfile, xfer, opt_compatibilityMode, opt_frame - 1, opt_frameCount); + di = new DicomImage(dfile, xfer, opt_compatibilityMode, opt_frame - 1, opt_frameCount); if (di == NULL) { OFLOG_FATAL(dcm2imgLogger, "Out of memory"); - return 1; + exitCode = 1; + goto cleanup; } if (di->getStatus() != EIS_Normal) { OFLOG_FATAL(dcm2imgLogger, DicomImage::getString(di->getStatus())); - return 1; + exitCode = 1; + goto cleanup; } /* create & set display function */ - DiDisplayFunction *disp = NULL; if (!opt_displayFile.empty()) { if (opt_displayFunction == 1) @@ -1749,14 +1775,19 @@ int main(int argc, char *argv[]) if (opt_frame != di->getFirstFrame() + 1) { OFLOG_FATAL(dcm2imgLogger, "cannot select frame " << opt_frame << ", invalid frame number"); - return 1; + exitCode = 1; + goto cleanup; } /* convert to grayscale if image is not monochrome */ if ((opt_convertToGrayscale) && (!di->isMonochrome())) { di = convertToGrayscale(di); - if (di == NULL) return 1; + if (di == NULL) + { + exitCode = 1; + goto cleanup; + } } /* process overlay parameters */ @@ -1766,7 +1797,11 @@ int main(int argc, char *argv[]) int result = processVOIParameters(di, opt_windowType, opt_windowParameter, opt_windowCenter, opt_windowWidth, opt_voiFunction, opt_ignoreVoiLutDepth, opt_roiLeft, opt_roiTop, opt_roiWidth, opt_roiHeight); - if (result) return result; + if (result) + { + exitCode = result; + goto cleanup; + } /* process presentation LUT parameters */ processPLUTParameters(di, opt_presShape); @@ -1782,7 +1817,11 @@ int main(int argc, char *argv[]) /* perform clipping */ di = performClipping(di, opt_useClip, opt_scaleType, opt_left, opt_top, opt_width, opt_height); - if (di == NULL) return 1; + if (di == NULL) + { + exitCode = 1; + goto cleanup; + } /* perform rotation */ performRotation(di, opt_rotateDegree); @@ -1794,7 +1833,11 @@ int main(int argc, char *argv[]) di = performScaling(di, opt_useClip, opt_scaleType, opt_left, opt_top, opt_width, opt_height, opt_scale_factor, opt_scale_size, opt_useInterpolation, opt_useAspectRatio); - if (di == NULL) return 1; + if (di == NULL) + { + exitCode = 1; + goto cleanup; + } /* write selected frame(s) to file */ @@ -1841,7 +1884,8 @@ int main(int argc, char *argv[]) if (ofile == NULL) { OFLOG_FATAL(dcm2imgLogger, "cannot create file " << ofname); - return 1; + exitCode = 1; + goto cleanup; } } else { /* output to stdout */ @@ -1991,20 +2035,25 @@ int main(int argc, char *argv[]) { if (fclose(ofile)) { - OFLOG_FATAL(dcm2imgLogger, "Error while closing file, content may be incomplete."); - return 1; + OFLOG_FATAL(dcm2imgLogger, "error while closing file, content may be incomplete"); + exitCode = 1; + goto cleanup; } } if (!result) { OFLOG_FATAL(dcm2imgLogger, "cannot write frame"); - return 1; + exitCode = 1; + goto cleanup; } } } /* done, now cleanup. */ OFLOG_INFO(dcm2imgLogger, "cleaning up memory"); + + cleanup: + delete di; delete disp; @@ -2026,5 +2075,5 @@ int main(int argc, char *argv[]) #endif /* WITH_OPENJPEG */ #endif /* BUILD_DCM2IMG_AS_DCM2KIMG */ - return 0; + return exitCode; } diff --git a/dcmdata/apps/CMakeLists.txt b/dcmdata/apps/CMakeLists.txt index 72caa400..4ba9859b 100644 --- a/dcmdata/apps/CMakeLists.txt +++ b/dcmdata/apps/CMakeLists.txt @@ -2,19 +2,20 @@ include_directories(${LIBXML_INCDIR}) # declare executables -foreach(PROGRAM dcm2xml dcmconv dcmcrle dcmdrle dcmdump dcmftest dcmgpdir dump2dcm xml2dcm stl2dcm pdf2dcm dcm2pdf img2dcm dcm2json cda2dcm dcm2cda) +foreach(PROGRAM dcm2xml dcmconv dcmcrle dcmdrle dcmdump dcmftest dcmgpdir dump2dcm xml2dcm stl2dcm pdf2dcm dcm2pdf img2dcm dcm2json cda2dcm dcm2cda json2dcm dcmencap dcmdecap) DCMTK_ADD_EXECUTABLE(${PROGRAM} ${PROGRAM}.cc) endforeach() + DCMTK_ADD_EXECUTABLE(dcmodify dcmodify.cc mdfconen.cc mdfdsman.cc) # make sure executables are linked to the corresponding libraries -foreach(PROGRAM dcm2xml dcmconv dcmcrle dcmdrle dcmdump dcmgpdir dcmodify dump2dcm xml2dcm stl2dcm pdf2dcm dcm2pdf img2dcm dcm2json cda2dcm dcm2cda) - DCMTK_TARGET_LINK_MODULES(${PROGRAM} dcmdata oflog ofstd) +foreach(PROGRAM dcm2xml dcmconv dcmcrle dcmdrle dcmdump dcmgpdir dcmodify dump2dcm stl2dcm pdf2dcm dcm2pdf dcm2json json2dcm cda2dcm dcm2cda dcmencap dcmdecap) + DCMTK_TARGET_LINK_MODULES(${PROGRAM} dcmdata) endforeach() DCMTK_TARGET_LINK_MODULES(dcmftest ofstd) -DCMTK_TARGET_LINK_MODULES(img2dcm i2d dcmdata) -DCMTK_TARGET_LINK_MODULES(xml2dcm dcmxml dcmdata oflog ofstd) +DCMTK_TARGET_LINK_MODULES(img2dcm i2d) +DCMTK_TARGET_LINK_MODULES(xml2dcm dcmxml) diff --git a/dcmdata/apps/Makefile.dep b/dcmdata/apps/Makefile.dep index b1823b72..ed038321 100644 --- a/dcmdata/apps/Makefile.dep +++ b/dcmdata/apps/Makefile.dep @@ -1,96 +1,28 @@ cda2dcm.o: cda2dcm.cc ../../config/include/dcmtk/config/osconfig.h \ - ../include/dcmtk/dcmdata/dctk.h ../include/dcmtk/dcmdata/dctypes.h \ - ../../oflog/include/dcmtk/oflog/oflog.h \ - ../../oflog/include/dcmtk/oflog/logger.h \ - ../../oflog/include/dcmtk/oflog/config.h \ + ../../ofstd/include/dcmtk/ofstd/ofstub.h \ ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ ../../ofstd/include/dcmtk/ofstd/ofcast.h \ ../../ofstd/include/dcmtk/ofstd/ofexport.h \ - ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ - ../../oflog/include/dcmtk/oflog/config/defines.h \ - ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ - ../../oflog/include/dcmtk/oflog/loglevel.h \ - ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../../ofstd/include/dcmtk/ofstd/oftypes.h \ - ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ ../../ofstd/include/dcmtk/ofstd/ofstring.h \ ../../ofstd/include/dcmtk/ofstd/ofstream.h \ - ../../oflog/include/dcmtk/oflog/tchar.h \ - ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ - ../../oflog/include/dcmtk/oflog/appender.h \ - ../../ofstd/include/dcmtk/ofstd/ofmem.h \ - ../../ofstd/include/dcmtk/ofstd/ofutil.h \ ../../ofstd/include/dcmtk/ofstd/oftraits.h \ - ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ - ../../oflog/include/dcmtk/oflog/layout.h \ - ../../oflog/include/dcmtk/oflog/streams.h \ - ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ - ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ - ../../oflog/include/dcmtk/oflog/spi/filter.h \ - ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ - ../../oflog/include/dcmtk/oflog/spi/logfact.h \ - ../../oflog/include/dcmtk/oflog/logmacro.h \ - ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ - ../../oflog/include/dcmtk/oflog/tracelog.h \ - ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcswap.h \ - ../include/dcmtk/dcmdata/dcerror.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ - ../include/dcmtk/dcmdata/dcxfer.h ../include/dcmtk/dcmdata/dcvr.h \ - ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ - ../include/dcmtk/dcmdata/dcistrma.h \ - ../../ofstd/include/dcmtk/ofstd/offile.h \ - ../../ofstd/include/dcmtk/ofstd/ofstd.h \ - ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ - ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../include/dcmtk/dcmdata/dcostrma.h ../include/dcmtk/dcmdata/dcuid.h \ - ../include/dcmtk/dcmdata/dctagkey.h \ - ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ - ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dcdicent.h \ - ../include/dcmtk/dcmdata/dchashdi.h ../include/dcmtk/dcmdata/dcdict.h \ - ../include/dcmtk/dcmdata/dcdeftag.h ../include/dcmtk/dcmdata/dcobject.h \ - ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dcelem.h \ - ../include/dcmtk/dcmdata/dcitem.h ../include/dcmtk/dcmdata/dclist.h \ - ../include/dcmtk/dcmdata/dcpcache.h ../include/dcmtk/dcmdata/dcmetinf.h \ - ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcsequen.h \ - ../include/dcmtk/dcmdata/dcfilefo.h ../include/dcmtk/dcmdata/dcdicdir.h \ - ../../ofstd/include/dcmtk/ofstd/ofmap.h \ - ../include/dcmtk/dcmdata/dcdirrec.h ../include/dcmtk/dcmdata/dcvrulup.h \ - ../include/dcmtk/dcmdata/dcvrul.h ../include/dcmtk/dcmdata/dcpixseq.h \ - ../include/dcmtk/dcmdata/dcofsetl.h ../include/dcmtk/dcmdata/dcbytstr.h \ - ../include/dcmtk/dcmdata/dcvrae.h ../include/dcmtk/dcmdata/dcvras.h \ - ../include/dcmtk/dcmdata/dcvrcs.h ../include/dcmtk/dcmdata/dcvrda.h \ - ../../ofstd/include/dcmtk/ofstd/ofdate.h \ - ../include/dcmtk/dcmdata/dcvrds.h ../include/dcmtk/dcmdata/dcvrdt.h \ - ../../ofstd/include/dcmtk/ofstd/ofdatime.h \ - ../../ofstd/include/dcmtk/ofstd/oftime.h \ - ../include/dcmtk/dcmdata/dcvris.h ../include/dcmtk/dcmdata/dcvrtm.h \ - ../include/dcmtk/dcmdata/dcvrui.h ../include/dcmtk/dcmdata/dcvrur.h \ - ../include/dcmtk/dcmdata/dcchrstr.h ../include/dcmtk/dcmdata/dcvrlo.h \ - ../include/dcmtk/dcmdata/dcvrlt.h ../include/dcmtk/dcmdata/dcvrpn.h \ - ../include/dcmtk/dcmdata/dcvrsh.h ../include/dcmtk/dcmdata/dcvrst.h \ - ../include/dcmtk/dcmdata/dcvruc.h ../include/dcmtk/dcmdata/dcvrut.h \ - ../include/dcmtk/dcmdata/dcvrobow.h ../include/dcmtk/dcmdata/dcpixel.h \ - ../include/dcmtk/dcmdata/dcvrpobw.h ../include/dcmtk/dcmdata/dcovlay.h \ - ../include/dcmtk/dcmdata/dcvrat.h ../include/dcmtk/dcmdata/dcvrss.h \ - ../include/dcmtk/dcmdata/dcvrus.h ../include/dcmtk/dcmdata/dcvrsl.h \ - ../include/dcmtk/dcmdata/dcvrsv.h ../include/dcmtk/dcmdata/dcvruv.h \ - ../include/dcmtk/dcmdata/dcvrfl.h ../include/dcmtk/dcmdata/dcvrfd.h \ - ../include/dcmtk/dcmdata/dcvrof.h ../include/dcmtk/dcmdata/dcvrod.h \ - ../include/dcmtk/dcmdata/dcvrol.h ../include/dcmtk/dcmdata/dcvrov.h \ - ../include/dcmtk/dcmdata/cmdlnarg.h ../include/dcmtk/dcmdata/dcencdoc.h \ - ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ - ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ - ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ - ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ - ../../ofstd/include/dcmtk/ofstd/ofexit.h + ../../ofstd/include/dcmtk/ofstd/oferror.h dcm2cda.o: dcm2cda.cc ../../config/include/dcmtk/config/osconfig.h \ + ../../ofstd/include/dcmtk/ofstd/ofstub.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h +dcm2json.o: dcm2json.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dctk.h ../include/dcmtk/dcmdata/dctypes.h \ ../../oflog/include/dcmtk/oflog/oflog.h \ ../../oflog/include/dcmtk/oflog/logger.h \ @@ -176,14 +108,19 @@ dcm2cda.o: dcm2cda.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dcvrfl.h ../include/dcmtk/dcmdata/dcvrfd.h \ ../include/dcmtk/dcmdata/dcvrof.h ../include/dcmtk/dcmdata/dcvrod.h \ ../include/dcmtk/dcmdata/dcvrol.h ../include/dcmtk/dcmdata/dcvrov.h \ - ../include/dcmtk/dcmdata/cmdlnarg.h \ + ../include/dcmtk/dcmdata/cmdlnarg.h ../include/dcmtk/dcmdata/dcjson.h \ ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ ../../ofstd/include/dcmtk/ofstd/ofexit.h \ - ../include/dcmtk/dcmdata/dcistrmz.h -dcm2json.o: dcm2json.cc ../../config/include/dcmtk/config/osconfig.h \ + ../../ofstd/include/dcmtk/ofstd/ofchrenc.h +dcm2pdf.o: dcm2pdf.cc ../../config/include/dcmtk/config/osconfig.h \ + ../../ofstd/include/dcmtk/ofstd/ofstub.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h +dcm2xml.o: dcm2xml.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dctk.h ../include/dcmtk/dcmdata/dctypes.h \ ../../oflog/include/dcmtk/oflog/oflog.h \ ../../oflog/include/dcmtk/oflog/logger.h \ @@ -269,14 +206,14 @@ dcm2json.o: dcm2json.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dcvrfl.h ../include/dcmtk/dcmdata/dcvrfd.h \ ../include/dcmtk/dcmdata/dcvrof.h ../include/dcmtk/dcmdata/dcvrod.h \ ../include/dcmtk/dcmdata/dcvrol.h ../include/dcmtk/dcmdata/dcvrov.h \ - ../include/dcmtk/dcmdata/cmdlnarg.h ../include/dcmtk/dcmdata/dcjson.h \ + ../include/dcmtk/dcmdata/cmdlnarg.h \ ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ ../../ofstd/include/dcmtk/ofstd/ofexit.h \ ../../ofstd/include/dcmtk/ofstd/ofchrenc.h -dcm2pdf.o: dcm2pdf.cc ../../config/include/dcmtk/config/osconfig.h \ +dcmconv.o: dcmconv.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dctk.h ../include/dcmtk/dcmdata/dctypes.h \ ../../oflog/include/dcmtk/oflog/oflog.h \ ../../oflog/include/dcmtk/oflog/logger.h \ @@ -368,8 +305,9 @@ dcm2pdf.o: dcm2pdf.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ ../../ofstd/include/dcmtk/ofstd/ofexit.h \ - ../include/dcmtk/dcmdata/dcistrmz.h -dcm2xml.o: dcm2xml.cc ../../config/include/dcmtk/config/osconfig.h \ + ../include/dcmtk/dcmdata/dcostrmz.h ../include/dcmtk/dcmdata/dcistrmz.h \ + ../../ofstd/include/dcmtk/ofstd/ofchrenc.h +dcmcrle.o: dcmcrle.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dctk.h ../include/dcmtk/dcmdata/dctypes.h \ ../../oflog/include/dcmtk/oflog/oflog.h \ ../../oflog/include/dcmtk/oflog/logger.h \ @@ -461,30 +399,49 @@ dcm2xml.o: dcm2xml.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ ../../ofstd/include/dcmtk/ofstd/ofexit.h \ - ../../ofstd/include/dcmtk/ofstd/ofchrenc.h -dcmconv.o: dcmconv.cc ../../config/include/dcmtk/config/osconfig.h \ - ../include/dcmtk/dcmdata/dctk.h ../include/dcmtk/dcmdata/dctypes.h \ - ../../oflog/include/dcmtk/oflog/oflog.h \ - ../../oflog/include/dcmtk/oflog/logger.h \ - ../../oflog/include/dcmtk/oflog/config.h \ + ../include/dcmtk/dcmdata/dcrleerg.h +dcmdecap.o: dcmdecap.cc ../../config/include/dcmtk/config/osconfig.h \ + ../include/dcmtk/dcmdata/cmdlnarg.h ../include/dcmtk/dcmdata/dcdefine.h \ ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ ../../ofstd/include/dcmtk/ofstd/ofcast.h \ ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ + ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../ofstd/include/dcmtk/ofstd/ofexit.h \ + ../include/dcmtk/dcmdata/dcuid.h ../include/dcmtk/dcmdata/dcistrmz.h \ + ../include/dcmtk/dcmdata/dcistrma.h ../include/dcmtk/dcmdata/dcxfer.h \ + ../include/dcmtk/dcmdata/dctypes.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ ../../oflog/include/dcmtk/oflog/config/defines.h \ ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ ../../oflog/include/dcmtk/oflog/loglevel.h \ ../../ofstd/include/dcmtk/ofstd/ofvector.h \ - ../../ofstd/include/dcmtk/ofstd/oftypes.h \ ../../oflog/include/dcmtk/oflog/tstring.h \ - ../../ofstd/include/dcmtk/ofstd/ofstring.h \ - ../../ofstd/include/dcmtk/ofstd/ofstream.h \ ../../oflog/include/dcmtk/oflog/tchar.h \ ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ ../../oflog/include/dcmtk/oflog/appender.h \ ../../ofstd/include/dcmtk/ofstd/ofmem.h \ ../../ofstd/include/dcmtk/ofstd/ofutil.h \ - ../../ofstd/include/dcmtk/ofstd/oftraits.h \ ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ ../../oflog/include/dcmtk/oflog/layout.h \ ../../oflog/include/dcmtk/oflog/streams.h \ @@ -496,67 +453,19 @@ dcmconv.o: dcmconv.cc ../../config/include/dcmtk/config/osconfig.h \ ../../oflog/include/dcmtk/oflog/logmacro.h \ ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ ../../oflog/include/dcmtk/oflog/tracelog.h \ - ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcswap.h \ - ../include/dcmtk/dcmdata/dcerror.h \ - ../../ofstd/include/dcmtk/ofstd/ofcond.h \ - ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ - ../../ofstd/include/dcmtk/ofstd/diag/push.def \ - ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ - ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ - ../include/dcmtk/dcmdata/dcxfer.h ../include/dcmtk/dcmdata/dcvr.h \ + ../include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ - ../include/dcmtk/dcmdata/dcistrma.h \ - ../../ofstd/include/dcmtk/ofstd/offile.h \ - ../../ofstd/include/dcmtk/ofstd/ofstd.h \ - ../../ofstd/include/dcmtk/ofstd/oflist.h \ - ../../ofstd/include/dcmtk/ofstd/oflimits.h \ - ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../include/dcmtk/dcmdata/dcostrma.h ../include/dcmtk/dcmdata/dcuid.h \ - ../include/dcmtk/dcmdata/dctagkey.h \ + ../include/dcmtk/dcmdata/dcdocdec.h ../include/dcmtk/dcmdata/dcfilefo.h \ + ../include/dcmtk/dcmdata/dcsequen.h ../include/dcmtk/dcmdata/dcelem.h \ + ../include/dcmtk/dcmdata/dcobject.h ../include/dcmtk/dcmdata/dcerror.h \ + ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dctagkey.h \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ - ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dcdicent.h \ - ../include/dcmtk/dcmdata/dchashdi.h ../include/dcmtk/dcmdata/dcdict.h \ - ../include/dcmtk/dcmdata/dcdeftag.h ../include/dcmtk/dcmdata/dcobject.h \ - ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dcelem.h \ - ../include/dcmtk/dcmdata/dcitem.h ../include/dcmtk/dcmdata/dclist.h \ - ../include/dcmtk/dcmdata/dcpcache.h ../include/dcmtk/dcmdata/dcmetinf.h \ - ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcsequen.h \ - ../include/dcmtk/dcmdata/dcfilefo.h ../include/dcmtk/dcmdata/dcdicdir.h \ - ../../ofstd/include/dcmtk/ofstd/ofmap.h \ - ../include/dcmtk/dcmdata/dcdirrec.h ../include/dcmtk/dcmdata/dcvrulup.h \ - ../include/dcmtk/dcmdata/dcvrul.h ../include/dcmtk/dcmdata/dcpixseq.h \ - ../include/dcmtk/dcmdata/dcofsetl.h ../include/dcmtk/dcmdata/dcbytstr.h \ - ../include/dcmtk/dcmdata/dcvrae.h ../include/dcmtk/dcmdata/dcvras.h \ - ../include/dcmtk/dcmdata/dcvrcs.h ../include/dcmtk/dcmdata/dcvrda.h \ - ../../ofstd/include/dcmtk/ofstd/ofdate.h \ - ../include/dcmtk/dcmdata/dcvrds.h ../include/dcmtk/dcmdata/dcvrdt.h \ - ../../ofstd/include/dcmtk/ofstd/ofdatime.h \ - ../../ofstd/include/dcmtk/ofstd/oftime.h \ - ../include/dcmtk/dcmdata/dcvris.h ../include/dcmtk/dcmdata/dcvrtm.h \ - ../include/dcmtk/dcmdata/dcvrui.h ../include/dcmtk/dcmdata/dcvrur.h \ - ../include/dcmtk/dcmdata/dcchrstr.h ../include/dcmtk/dcmdata/dcvrlo.h \ - ../include/dcmtk/dcmdata/dcvrlt.h ../include/dcmtk/dcmdata/dcvrpn.h \ - ../include/dcmtk/dcmdata/dcvrsh.h ../include/dcmtk/dcmdata/dcvrst.h \ - ../include/dcmtk/dcmdata/dcvruc.h ../include/dcmtk/dcmdata/dcvrut.h \ - ../include/dcmtk/dcmdata/dcvrobow.h ../include/dcmtk/dcmdata/dcpixel.h \ - ../include/dcmtk/dcmdata/dcvrpobw.h ../include/dcmtk/dcmdata/dcovlay.h \ - ../include/dcmtk/dcmdata/dcvrat.h ../include/dcmtk/dcmdata/dcvrss.h \ - ../include/dcmtk/dcmdata/dcvrus.h ../include/dcmtk/dcmdata/dcvrsl.h \ - ../include/dcmtk/dcmdata/dcvrsv.h ../include/dcmtk/dcmdata/dcvruv.h \ - ../include/dcmtk/dcmdata/dcvrfl.h ../include/dcmtk/dcmdata/dcvrfd.h \ - ../include/dcmtk/dcmdata/dcvrof.h ../include/dcmtk/dcmdata/dcvrod.h \ - ../include/dcmtk/dcmdata/dcvrol.h ../include/dcmtk/dcmdata/dcvrov.h \ - ../include/dcmtk/dcmdata/cmdlnarg.h \ - ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ - ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ - ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ - ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ - ../../ofstd/include/dcmtk/ofstd/ofexit.h \ - ../include/dcmtk/dcmdata/dcostrmz.h ../include/dcmtk/dcmdata/dcistrmz.h \ - ../../ofstd/include/dcmtk/ofstd/ofchrenc.h -dcmcrle.o: dcmcrle.cc ../../config/include/dcmtk/config/osconfig.h \ + ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dclist.h \ + ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcitem.h \ + ../include/dcmtk/dcmdata/dcpcache.h ../include/dcmtk/dcmdata/dcdict.h \ + ../include/dcmtk/dcmdata/dchashdi.h +dcmdrle.o: dcmdrle.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dctk.h ../include/dcmtk/dcmdata/dctypes.h \ ../../oflog/include/dcmtk/oflog/oflog.h \ ../../oflog/include/dcmtk/oflog/logger.h \ @@ -648,8 +557,10 @@ dcmcrle.o: dcmcrle.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ ../../ofstd/include/dcmtk/ofstd/ofexit.h \ - ../include/dcmtk/dcmdata/dcrleerg.h -dcmdrle.o: dcmdrle.cc ../../config/include/dcmtk/config/osconfig.h \ + ../include/dcmtk/dcmdata/dcrledrg.h +dcmdump.o: dcmdump.cc ../../config/include/dcmtk/config/osconfig.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ ../include/dcmtk/dcmdata/dctk.h ../include/dcmtk/dcmdata/dctypes.h \ ../../oflog/include/dcmtk/oflog/oflog.h \ ../../oflog/include/dcmtk/oflog/logger.h \ @@ -657,7 +568,6 @@ dcmdrle.o: dcmdrle.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ ../../ofstd/include/dcmtk/ofstd/ofcast.h \ ../../ofstd/include/dcmtk/ofstd/ofexport.h \ - ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ ../../oflog/include/dcmtk/oflog/config/defines.h \ ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ ../../oflog/include/dcmtk/oflog/loglevel.h \ @@ -665,7 +575,6 @@ dcmdrle.o: dcmdrle.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/oftypes.h \ ../../oflog/include/dcmtk/oflog/tstring.h \ ../../ofstd/include/dcmtk/ofstd/ofstring.h \ - ../../ofstd/include/dcmtk/ofstd/ofstream.h \ ../../oflog/include/dcmtk/oflog/tchar.h \ ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ ../../oflog/include/dcmtk/oflog/appender.h \ @@ -741,30 +650,48 @@ dcmdrle.o: dcmdrle.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ ../../ofstd/include/dcmtk/ofstd/ofexit.h \ - ../include/dcmtk/dcmdata/dcrledrg.h -dcmdump.o: dcmdump.cc ../../config/include/dcmtk/config/osconfig.h \ - ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../include/dcmtk/dcmdata/dcistrmz.h \ + ../../ofstd/include/dcmtk/ofstd/ofchrenc.h +dcmencap.o: dcmencap.cc ../../config/include/dcmtk/config/osconfig.h \ + ../include/dcmtk/dcmdata/dcencdoc.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ - ../include/dcmtk/dcmdata/dctk.h ../include/dcmtk/dcmdata/dctypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcfilefo.h \ + ../include/dcmtk/dcmdata/dcsequen.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../include/dcmtk/dcmdata/dcelem.h ../include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../include/dcmtk/dcmdata/dcerror.h ../include/dcmtk/dcmdata/dcxfer.h \ + ../include/dcmtk/dcmdata/dctypes.h \ ../../oflog/include/dcmtk/oflog/oflog.h \ ../../oflog/include/dcmtk/oflog/logger.h \ ../../oflog/include/dcmtk/oflog/config.h \ - ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ - ../../ofstd/include/dcmtk/ofstd/ofcast.h \ - ../../ofstd/include/dcmtk/ofstd/ofexport.h \ ../../oflog/include/dcmtk/oflog/config/defines.h \ ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ ../../oflog/include/dcmtk/oflog/loglevel.h \ ../../ofstd/include/dcmtk/ofstd/ofvector.h \ - ../../ofstd/include/dcmtk/ofstd/oftypes.h \ ../../oflog/include/dcmtk/oflog/tstring.h \ - ../../ofstd/include/dcmtk/ofstd/ofstring.h \ ../../oflog/include/dcmtk/oflog/tchar.h \ ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ ../../oflog/include/dcmtk/oflog/appender.h \ ../../ofstd/include/dcmtk/ofstd/ofmem.h \ ../../ofstd/include/dcmtk/ofstd/ofutil.h \ - ../../ofstd/include/dcmtk/ofstd/oftraits.h \ ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ ../../oflog/include/dcmtk/oflog/layout.h \ ../../oflog/include/dcmtk/oflog/streams.h \ @@ -776,66 +703,20 @@ dcmdump.o: dcmdump.cc ../../config/include/dcmtk/config/osconfig.h \ ../../oflog/include/dcmtk/oflog/logmacro.h \ ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ ../../oflog/include/dcmtk/oflog/tracelog.h \ - ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcswap.h \ - ../include/dcmtk/dcmdata/dcerror.h \ - ../../ofstd/include/dcmtk/ofstd/ofcond.h \ - ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ - ../../ofstd/include/dcmtk/ofstd/diag/push.def \ - ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ - ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ - ../include/dcmtk/dcmdata/dcxfer.h ../include/dcmtk/dcmdata/dcvr.h \ - ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ - ../include/dcmtk/dcmdata/dcistrma.h \ - ../../ofstd/include/dcmtk/ofstd/offile.h \ - ../../ofstd/include/dcmtk/ofstd/ofstd.h \ - ../../ofstd/include/dcmtk/ofstd/oflist.h \ - ../../ofstd/include/dcmtk/ofstd/oflimits.h \ - ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../include/dcmtk/dcmdata/dcostrma.h ../include/dcmtk/dcmdata/dcuid.h \ - ../include/dcmtk/dcmdata/dctagkey.h \ + ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dctagkey.h \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ - ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dcdicent.h \ - ../include/dcmtk/dcmdata/dchashdi.h ../include/dcmtk/dcmdata/dcdict.h \ - ../include/dcmtk/dcmdata/dcdeftag.h ../include/dcmtk/dcmdata/dcobject.h \ - ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dcelem.h \ - ../include/dcmtk/dcmdata/dcitem.h ../include/dcmtk/dcmdata/dclist.h \ - ../include/dcmtk/dcmdata/dcpcache.h ../include/dcmtk/dcmdata/dcmetinf.h \ - ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcsequen.h \ - ../include/dcmtk/dcmdata/dcfilefo.h ../include/dcmtk/dcmdata/dcdicdir.h \ - ../../ofstd/include/dcmtk/ofstd/ofmap.h \ - ../include/dcmtk/dcmdata/dcdirrec.h ../include/dcmtk/dcmdata/dcvrulup.h \ - ../include/dcmtk/dcmdata/dcvrul.h ../include/dcmtk/dcmdata/dcpixseq.h \ - ../include/dcmtk/dcmdata/dcofsetl.h ../include/dcmtk/dcmdata/dcbytstr.h \ - ../include/dcmtk/dcmdata/dcvrae.h ../include/dcmtk/dcmdata/dcvras.h \ - ../include/dcmtk/dcmdata/dcvrcs.h ../include/dcmtk/dcmdata/dcvrda.h \ - ../../ofstd/include/dcmtk/ofstd/ofdate.h \ - ../include/dcmtk/dcmdata/dcvrds.h ../include/dcmtk/dcmdata/dcvrdt.h \ - ../../ofstd/include/dcmtk/ofstd/ofdatime.h \ - ../../ofstd/include/dcmtk/ofstd/oftime.h \ - ../include/dcmtk/dcmdata/dcvris.h ../include/dcmtk/dcmdata/dcvrtm.h \ - ../include/dcmtk/dcmdata/dcvrui.h ../include/dcmtk/dcmdata/dcvrur.h \ - ../include/dcmtk/dcmdata/dcchrstr.h ../include/dcmtk/dcmdata/dcvrlo.h \ - ../include/dcmtk/dcmdata/dcvrlt.h ../include/dcmtk/dcmdata/dcvrpn.h \ - ../include/dcmtk/dcmdata/dcvrsh.h ../include/dcmtk/dcmdata/dcvrst.h \ - ../include/dcmtk/dcmdata/dcvruc.h ../include/dcmtk/dcmdata/dcvrut.h \ - ../include/dcmtk/dcmdata/dcvrobow.h ../include/dcmtk/dcmdata/dcpixel.h \ - ../include/dcmtk/dcmdata/dcvrpobw.h ../include/dcmtk/dcmdata/dcovlay.h \ - ../include/dcmtk/dcmdata/dcvrat.h ../include/dcmtk/dcmdata/dcvrss.h \ - ../include/dcmtk/dcmdata/dcvrus.h ../include/dcmtk/dcmdata/dcvrsl.h \ - ../include/dcmtk/dcmdata/dcvrsv.h ../include/dcmtk/dcmdata/dcvruv.h \ - ../include/dcmtk/dcmdata/dcvrfl.h ../include/dcmtk/dcmdata/dcvrfd.h \ - ../include/dcmtk/dcmdata/dcvrof.h ../include/dcmtk/dcmdata/dcvrod.h \ - ../include/dcmtk/dcmdata/dcvrol.h ../include/dcmtk/dcmdata/dcvrov.h \ - ../include/dcmtk/dcmdata/cmdlnarg.h \ - ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ + ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dclist.h \ + ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcitem.h \ + ../include/dcmtk/dcmdata/dcpcache.h \ ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ - ../../ofstd/include/dcmtk/ofstd/ofexit.h \ - ../include/dcmtk/dcmdata/dcistrmz.h \ - ../../ofstd/include/dcmtk/ofstd/ofchrenc.h + ../include/dcmtk/dcmdata/cmdlnarg.h ../include/dcmtk/dcmdata/dcuid.h \ + ../include/dcmtk/dcmdata/dcdict.h ../include/dcmtk/dcmdata/dchashdi.h \ + ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ + ../../ofstd/include/dcmtk/ofstd/ofexit.h dcmftest.o: dcmftest.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dcmetinf.h ../include/dcmtk/dcmdata/dcitem.h \ ../../ofstd/include/dcmtk/ofstd/offile.h \ @@ -1130,6 +1011,72 @@ img2dcm.o: img2dcm.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/libi2d/i2dplop.h \ ../include/dcmtk/dcmdata/dcmxml/xml2dcm.h \ ../include/dcmtk/dcmdata/dcmxml/dcxmldf.h +json2dcm.o: json2dcm.cc ../../config/include/dcmtk/config/osconfig.h \ + ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ + ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../ofstd/include/dcmtk/ofstd/ofexit.h \ + ../include/dcmtk/dcmdata/cmdlnarg.h ../include/dcmtk/dcmdata/dcdefine.h \ + ../include/dcmtk/dcmdata/dcostrmz.h ../include/dcmtk/dcmdata/dcostrma.h \ + ../include/dcmtk/dcmdata/dcxfer.h ../include/dcmtk/dcmdata/dctypes.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../include/dcmtk/dcmdata/dcfilefo.h ../include/dcmtk/dcmdata/dcsequen.h \ + ../include/dcmtk/dcmdata/dcelem.h ../include/dcmtk/dcmdata/dcobject.h \ + ../include/dcmtk/dcmdata/dcerror.h ../include/dcmtk/dcmdata/dctag.h \ + ../include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dclist.h \ + ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcitem.h \ + ../include/dcmtk/dcmdata/dcpcache.h ../include/dcmtk/dcmdata/dcdeftag.h \ + ../include/dcmtk/dcmdata/dcuid.h ../include/dcmtk/dcmdata/dcdict.h \ + ../include/dcmtk/dcmdata/dchashdi.h ../include/dcmtk/dcmdata/dcjsonrd.h \ + ../../ofstd/include/dcmtk/ofstd/ofjsmn.h mdfconen.o: mdfconen.cc ../../config/include/dcmtk/config/osconfig.h \ mdfconen.h ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/oftypes.h \ @@ -1288,189 +1235,43 @@ mdfdsman.o: mdfdsman.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ ../../ofstd/include/dcmtk/ofstd/ofconsol.h pdf2dcm.o: pdf2dcm.cc ../../config/include/dcmtk/config/osconfig.h \ - ../include/dcmtk/dcmdata/dctk.h ../include/dcmtk/dcmdata/dctypes.h \ - ../../oflog/include/dcmtk/oflog/oflog.h \ - ../../oflog/include/dcmtk/oflog/logger.h \ - ../../oflog/include/dcmtk/oflog/config.h \ + ../../ofstd/include/dcmtk/ofstd/ofstub.h \ ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ ../../ofstd/include/dcmtk/ofstd/ofcast.h \ ../../ofstd/include/dcmtk/ofstd/ofexport.h \ - ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ - ../../oflog/include/dcmtk/oflog/config/defines.h \ - ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ - ../../oflog/include/dcmtk/oflog/loglevel.h \ - ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../../ofstd/include/dcmtk/ofstd/oftypes.h \ - ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ ../../ofstd/include/dcmtk/ofstd/ofstring.h \ ../../ofstd/include/dcmtk/ofstd/ofstream.h \ - ../../oflog/include/dcmtk/oflog/tchar.h \ - ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ - ../../oflog/include/dcmtk/oflog/appender.h \ - ../../ofstd/include/dcmtk/ofstd/ofmem.h \ - ../../ofstd/include/dcmtk/ofstd/ofutil.h \ ../../ofstd/include/dcmtk/ofstd/oftraits.h \ - ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ - ../../oflog/include/dcmtk/oflog/layout.h \ - ../../oflog/include/dcmtk/oflog/streams.h \ - ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ - ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ - ../../oflog/include/dcmtk/oflog/spi/filter.h \ - ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ - ../../oflog/include/dcmtk/oflog/spi/logfact.h \ - ../../oflog/include/dcmtk/oflog/logmacro.h \ - ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ - ../../oflog/include/dcmtk/oflog/tracelog.h \ - ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcswap.h \ - ../include/dcmtk/dcmdata/dcerror.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ - ../include/dcmtk/dcmdata/dcxfer.h ../include/dcmtk/dcmdata/dcvr.h \ - ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ - ../include/dcmtk/dcmdata/dcistrma.h \ - ../../ofstd/include/dcmtk/ofstd/offile.h \ - ../../ofstd/include/dcmtk/ofstd/ofstd.h \ - ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ - ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../include/dcmtk/dcmdata/dcostrma.h ../include/dcmtk/dcmdata/dcuid.h \ - ../include/dcmtk/dcmdata/dctagkey.h \ - ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ - ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dcdicent.h \ - ../include/dcmtk/dcmdata/dchashdi.h ../include/dcmtk/dcmdata/dcdict.h \ - ../include/dcmtk/dcmdata/dcdeftag.h ../include/dcmtk/dcmdata/dcobject.h \ - ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dcelem.h \ - ../include/dcmtk/dcmdata/dcitem.h ../include/dcmtk/dcmdata/dclist.h \ - ../include/dcmtk/dcmdata/dcpcache.h ../include/dcmtk/dcmdata/dcmetinf.h \ - ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcsequen.h \ - ../include/dcmtk/dcmdata/dcfilefo.h ../include/dcmtk/dcmdata/dcdicdir.h \ - ../../ofstd/include/dcmtk/ofstd/ofmap.h \ - ../include/dcmtk/dcmdata/dcdirrec.h ../include/dcmtk/dcmdata/dcvrulup.h \ - ../include/dcmtk/dcmdata/dcvrul.h ../include/dcmtk/dcmdata/dcpixseq.h \ - ../include/dcmtk/dcmdata/dcofsetl.h ../include/dcmtk/dcmdata/dcbytstr.h \ - ../include/dcmtk/dcmdata/dcvrae.h ../include/dcmtk/dcmdata/dcvras.h \ - ../include/dcmtk/dcmdata/dcvrcs.h ../include/dcmtk/dcmdata/dcvrda.h \ - ../../ofstd/include/dcmtk/ofstd/ofdate.h \ - ../include/dcmtk/dcmdata/dcvrds.h ../include/dcmtk/dcmdata/dcvrdt.h \ - ../../ofstd/include/dcmtk/ofstd/ofdatime.h \ - ../../ofstd/include/dcmtk/ofstd/oftime.h \ - ../include/dcmtk/dcmdata/dcvris.h ../include/dcmtk/dcmdata/dcvrtm.h \ - ../include/dcmtk/dcmdata/dcvrui.h ../include/dcmtk/dcmdata/dcvrur.h \ - ../include/dcmtk/dcmdata/dcchrstr.h ../include/dcmtk/dcmdata/dcvrlo.h \ - ../include/dcmtk/dcmdata/dcvrlt.h ../include/dcmtk/dcmdata/dcvrpn.h \ - ../include/dcmtk/dcmdata/dcvrsh.h ../include/dcmtk/dcmdata/dcvrst.h \ - ../include/dcmtk/dcmdata/dcvruc.h ../include/dcmtk/dcmdata/dcvrut.h \ - ../include/dcmtk/dcmdata/dcvrobow.h ../include/dcmtk/dcmdata/dcpixel.h \ - ../include/dcmtk/dcmdata/dcvrpobw.h ../include/dcmtk/dcmdata/dcovlay.h \ - ../include/dcmtk/dcmdata/dcvrat.h ../include/dcmtk/dcmdata/dcvrss.h \ - ../include/dcmtk/dcmdata/dcvrus.h ../include/dcmtk/dcmdata/dcvrsl.h \ - ../include/dcmtk/dcmdata/dcvrsv.h ../include/dcmtk/dcmdata/dcvruv.h \ - ../include/dcmtk/dcmdata/dcvrfl.h ../include/dcmtk/dcmdata/dcvrfd.h \ - ../include/dcmtk/dcmdata/dcvrof.h ../include/dcmtk/dcmdata/dcvrod.h \ - ../include/dcmtk/dcmdata/dcvrol.h ../include/dcmtk/dcmdata/dcvrov.h \ - ../include/dcmtk/dcmdata/cmdlnarg.h ../include/dcmtk/dcmdata/dcencdoc.h \ - ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ - ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ - ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ - ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ - ../../ofstd/include/dcmtk/ofstd/ofexit.h + ../../ofstd/include/dcmtk/ofstd/oferror.h stl2dcm.o: stl2dcm.cc ../../config/include/dcmtk/config/osconfig.h \ - ../include/dcmtk/dcmdata/dctk.h ../include/dcmtk/dcmdata/dctypes.h \ - ../../oflog/include/dcmtk/oflog/oflog.h \ - ../../oflog/include/dcmtk/oflog/logger.h \ - ../../oflog/include/dcmtk/oflog/config.h \ + ../../ofstd/include/dcmtk/ofstd/ofstub.h \ ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ ../../ofstd/include/dcmtk/ofstd/ofcast.h \ ../../ofstd/include/dcmtk/ofstd/ofexport.h \ - ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ - ../../oflog/include/dcmtk/oflog/config/defines.h \ - ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ - ../../oflog/include/dcmtk/oflog/loglevel.h \ - ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../../ofstd/include/dcmtk/ofstd/oftypes.h \ - ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ ../../ofstd/include/dcmtk/ofstd/ofstring.h \ ../../ofstd/include/dcmtk/ofstd/ofstream.h \ - ../../oflog/include/dcmtk/oflog/tchar.h \ - ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ - ../../oflog/include/dcmtk/oflog/appender.h \ - ../../ofstd/include/dcmtk/ofstd/ofmem.h \ - ../../ofstd/include/dcmtk/ofstd/ofutil.h \ ../../ofstd/include/dcmtk/ofstd/oftraits.h \ - ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ - ../../oflog/include/dcmtk/oflog/layout.h \ - ../../oflog/include/dcmtk/oflog/streams.h \ - ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ - ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ - ../../oflog/include/dcmtk/oflog/spi/filter.h \ - ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ - ../../oflog/include/dcmtk/oflog/spi/logfact.h \ - ../../oflog/include/dcmtk/oflog/logmacro.h \ - ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ - ../../oflog/include/dcmtk/oflog/tracelog.h \ - ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcswap.h \ - ../include/dcmtk/dcmdata/dcerror.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ - ../include/dcmtk/dcmdata/dcxfer.h ../include/dcmtk/dcmdata/dcvr.h \ - ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ - ../include/dcmtk/dcmdata/dcistrma.h \ - ../../ofstd/include/dcmtk/ofstd/offile.h \ - ../../ofstd/include/dcmtk/ofstd/ofstd.h \ - ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ - ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../include/dcmtk/dcmdata/dcostrma.h ../include/dcmtk/dcmdata/dcuid.h \ - ../include/dcmtk/dcmdata/dctagkey.h \ - ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ - ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dcdicent.h \ - ../include/dcmtk/dcmdata/dchashdi.h ../include/dcmtk/dcmdata/dcdict.h \ - ../include/dcmtk/dcmdata/dcdeftag.h ../include/dcmtk/dcmdata/dcobject.h \ - ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dcelem.h \ - ../include/dcmtk/dcmdata/dcitem.h ../include/dcmtk/dcmdata/dclist.h \ - ../include/dcmtk/dcmdata/dcpcache.h ../include/dcmtk/dcmdata/dcmetinf.h \ - ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcsequen.h \ - ../include/dcmtk/dcmdata/dcfilefo.h ../include/dcmtk/dcmdata/dcdicdir.h \ - ../../ofstd/include/dcmtk/ofstd/ofmap.h \ - ../include/dcmtk/dcmdata/dcdirrec.h ../include/dcmtk/dcmdata/dcvrulup.h \ - ../include/dcmtk/dcmdata/dcvrul.h ../include/dcmtk/dcmdata/dcpixseq.h \ - ../include/dcmtk/dcmdata/dcofsetl.h ../include/dcmtk/dcmdata/dcbytstr.h \ - ../include/dcmtk/dcmdata/dcvrae.h ../include/dcmtk/dcmdata/dcvras.h \ - ../include/dcmtk/dcmdata/dcvrcs.h ../include/dcmtk/dcmdata/dcvrda.h \ - ../../ofstd/include/dcmtk/ofstd/ofdate.h \ - ../include/dcmtk/dcmdata/dcvrds.h ../include/dcmtk/dcmdata/dcvrdt.h \ - ../../ofstd/include/dcmtk/ofstd/ofdatime.h \ - ../../ofstd/include/dcmtk/ofstd/oftime.h \ - ../include/dcmtk/dcmdata/dcvris.h ../include/dcmtk/dcmdata/dcvrtm.h \ - ../include/dcmtk/dcmdata/dcvrui.h ../include/dcmtk/dcmdata/dcvrur.h \ - ../include/dcmtk/dcmdata/dcchrstr.h ../include/dcmtk/dcmdata/dcvrlo.h \ - ../include/dcmtk/dcmdata/dcvrlt.h ../include/dcmtk/dcmdata/dcvrpn.h \ - ../include/dcmtk/dcmdata/dcvrsh.h ../include/dcmtk/dcmdata/dcvrst.h \ - ../include/dcmtk/dcmdata/dcvruc.h ../include/dcmtk/dcmdata/dcvrut.h \ - ../include/dcmtk/dcmdata/dcvrobow.h ../include/dcmtk/dcmdata/dcpixel.h \ - ../include/dcmtk/dcmdata/dcvrpobw.h ../include/dcmtk/dcmdata/dcovlay.h \ - ../include/dcmtk/dcmdata/dcvrat.h ../include/dcmtk/dcmdata/dcvrss.h \ - ../include/dcmtk/dcmdata/dcvrus.h ../include/dcmtk/dcmdata/dcvrsl.h \ - ../include/dcmtk/dcmdata/dcvrsv.h ../include/dcmtk/dcmdata/dcvruv.h \ - ../include/dcmtk/dcmdata/dcvrfl.h ../include/dcmtk/dcmdata/dcvrfd.h \ - ../include/dcmtk/dcmdata/dcvrof.h ../include/dcmtk/dcmdata/dcvrod.h \ - ../include/dcmtk/dcmdata/dcvrol.h ../include/dcmtk/dcmdata/dcvrov.h \ - ../include/dcmtk/dcmdata/cmdlnarg.h ../include/dcmtk/dcmdata/dcencdoc.h \ - ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ - ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ - ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ - ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ - ../../ofstd/include/dcmtk/ofstd/ofexit.h + ../../ofstd/include/dcmtk/ofstd/oferror.h xml2dcm.o: xml2dcm.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dctk.h ../include/dcmtk/dcmdata/dctypes.h \ ../../oflog/include/dcmtk/oflog/oflog.h \ diff --git a/dcmdata/apps/Makefile.in b/dcmdata/apps/Makefile.in index 14bc1d34..21395b68 100644 --- a/dcmdata/apps/Makefile.in +++ b/dcmdata/apps/Makefile.in @@ -25,10 +25,12 @@ LIBDCMXML = -ldcmxml objs = dcmftest.o dcmconv.o dcmdump.o dump2dcm.o dcmgpdir.o dcm2xml.o \ xml2dcm.o dcmcrle.o dcmdrle.o dcmodify.o mdfdsman.o mdfconen.o \ - cda2dcm.o stl2dcm.o pdf2dcm.o dcm2pdf.o dcm2cda.o img2dcm.o dcm2json.o + cda2dcm.o stl2dcm.o pdf2dcm.o dcm2pdf.o dcm2cda.o img2dcm.o dcm2json.o \ + json2dcm.o dcmencap.o dcmdecap.o progs = dcmftest dcmconv dcmdump dump2dcm dcmgpdir dcm2xml xml2dcm dcmcrle \ - dcmdrle dcmodify pdf2dcm stl2dcm cda2dcm dcm2pdf dcm2cda img2dcm dcm2json + dcmdrle dcmodify pdf2dcm stl2dcm cda2dcm dcm2pdf dcm2cda img2dcm dcm2json \ + json2dcm dcmencap dcmdecap all: $(progs) @@ -85,6 +87,15 @@ img2dcm: img2dcm.o dcm2json: dcm2json.o $(CXX) $(CXXFLAGS) $(LIBDIRS) $(LDFLAGS) -o $@ $@.o $(LOCALLIBS) $(LIBS) +json2dcm: json2dcm.o + $(CXX) $(CXXFLAGS) $(LIBDIRS) $(LDFLAGS) -o $@ $@.o $(LOCALLIBS) $(LIBS) + +dcmencap: dcmencap.o + $(CXX) $(CXXFLAGS) $(LIBDIRS) $(LDFLAGS) -o $@ $@.o $(LOCALLIBS) $(LIBS) + +dcmdecap: dcmdecap.o + $(CXX) $(CXXFLAGS) $(LIBDIRS) $(LDFLAGS) -o $@ $@.o $(LOCALLIBS) $(LIBS) + install: all $(configdir)/mkinstalldirs $(DESTDIR)$(bindir) diff --git a/dcmdata/apps/cda2dcm.cc b/dcmdata/apps/cda2dcm.cc index e44ba7ad..6857ad4e 100644 --- a/dcmdata/apps/cda2dcm.cc +++ b/dcmdata/apps/cda2dcm.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2018-2019, OFFIS e.V. + * Copyright (C) 2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -13,131 +13,43 @@ * * Module: dcmdata * - * Author: Pedro Arizpe + * Authors: Marco Eichelberg * - * Purpose: Encapsulate CDA file into a DICOM file + * Purpose: Proxy stub that calls dcmencap * */ -#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first*/ -#include "dcmtk/dcmdata/dctk.h" -#include "dcmtk/dcmdata/dcencdoc.h" -#include "dcmtk/ofstd/ofconapp.h" +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/ofstd/ofstub.h" +#include "dcmtk/ofstd/ofstd.h" +#include -#ifdef WITH_ZLIB -#include /* for zlibVersion() */ -#endif - -#define OFFIS_CONSOLE_APPLICATION "cda2dcm" - -static OFLogger cda2dcmLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION); - -static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" -OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; - -int main(int argc, char *argv[]) +int main(int argc, char** argv) { - OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Encapsulate CDA file into DICOM format", rcsid); - OFCommandLine cmd; - int errorCode = EXITCODE_NO_ERROR; - OFCondition result = EC_Normal; - DcmFileFormat fileformat; - DcmEncapsulatedDocument encapsulator; - OFLOG_TRACE(cda2dcmLogger, "Including necessary options"); - encapsulator.addCDACommandlineOptions(cmd); - OFLOG_TRACE(cda2dcmLogger, "Evaluating command line"); - prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); - - if (app.parseCommandLine(cmd, argc, argv)) { - OFLOG_TRACE (cda2dcmLogger, "Checking exclusive options first"); - if (cmd.hasExclusiveOption()) + // create an argv array that is one entry larger than the one specified by the user + char **my_argv = new char *[argc+2]; + char filetype[100]; + OFStandard::strlcpy(filetype, "--filetype-cda", sizeof(filetype)); + + // copy arguments, then add file type argument and NULL pointer + memcpy(my_argv, argv, argc * sizeof(char *)); + my_argv[argc] = filetype; + my_argv[argc+1] = NULL; + + // call stub + int result; + if (argc == 1) { - if (cmd.findOption("--version")) - { - app.printHeader(OFTrue /*print host identifier*/); - COUT << OFendl << "External libraries used: "; -#ifdef WITH_ZLIB - COUT << OFendl << "- ZLIB, Version " << zlibVersion() << OFendl; -#else - COUT << " none" << OFendl; -#endif - return EXITCODE_NO_ERROR; - } + // no command line arguments given. Just forward call. + result = OFstub_main(argc, argv, "cda2dcm", "dcmencap"); + } + else + { + // Forward call with additional command line argument. + result = OFstub_main(argc+1, my_argv, "cda2dcm", "dcmencap"); } - encapsulator.parseArguments(app, cmd); - } - //print resource identifier - OFLOG_DEBUG(cda2dcmLogger, rcsid << OFendl); - - OFLOG_DEBUG(cda2dcmLogger, "making sure data dictionary is loaded"); - if (!dcmDataDict.isDictionaryLoaded()) - { - OFLOG_WARN(cda2dcmLogger, "no data dictionary loaded, check environment variable: " - << DCM_DICT_ENVIRONMENT_VARIABLE); - } - OFLOG_TRACE(cda2dcmLogger, "Creating identifiers (and reading series data)"); - result = encapsulator.createIdentifiers(cda2dcmLogger); - if (result.bad()) - { - OFLOG_FATAL(cda2dcmLogger, "There was an error while reading the series data"); - return EXITCODE_INVALID_INPUT_FILE; - } - OFLOG_DEBUG(cda2dcmLogger, "Fetching CDA Data"); - errorCode = encapsulator.getCDAData(encapsulator.getInputFileName().c_str(), cda2dcmLogger); - if (errorCode != EXITCODE_NO_ERROR) - { - OFLOG_ERROR(cda2dcmLogger, "There was a problem with the CDA File"); - return errorCode; - } - else - { - OFLOG_INFO(cda2dcmLogger, "creating encapsulated CDA object"); - errorCode = encapsulator.insertEncapsulatedDocument(fileformat.getDataset(), cda2dcmLogger); - } - if (errorCode != EXITCODE_NO_ERROR) - { - OFLOG_ERROR(cda2dcmLogger, "unable to create CDA encapsulation to DICOM format"); - return errorCode; - } - OFLOG_INFO(cda2dcmLogger, "Generating an instance number that is guaranteed to be unique within a series."); - result = encapsulator.createHeader(fileformat.getDataset(), cda2dcmLogger); - if (result.bad()) - { - OFLOG_ERROR(cda2dcmLogger, "unable to create DICOM header: " << result.text()); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - OFLOG_INFO(cda2dcmLogger, "writing encapsulated CDA object as file " << encapsulator.getOutputFileName()); - - OFLOG_INFO(cda2dcmLogger, "Check if new output transfer syntax is possible"); - - DcmXfer opt_oxferSyn(encapsulator.getTransferSyntax()); - - fileformat.getDataset()->chooseRepresentation(encapsulator.getTransferSyntax(), NULL); - if (fileformat.getDataset()->canWriteXfer(encapsulator.getTransferSyntax())) - { - OFLOG_INFO(cda2dcmLogger, "Output transfer syntax " << opt_oxferSyn.getXferName() << " can be written"); - } - else { - OFLOG_ERROR(cda2dcmLogger, "No conversion to transfer syntax " << opt_oxferSyn.getXferName() << " possible!"); - return EXITCODE_COMMANDLINE_SYNTAX_ERROR; - } - OFLOG_INFO(cda2dcmLogger, "Checking for DICOM key overriding"); - result = encapsulator.applyOverrideKeys(fileformat.getDataset()); - if (result.bad()) - { - OFLOG_ERROR(cda2dcmLogger, "There was a problem while overriding a key:" << OFendl - << result.text()); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - OFLOG_INFO(cda2dcmLogger, "write converted DICOM file with metaheader"); - result = encapsulator.saveFile(fileformat); - if (result.bad()) - { - OFLOG_ERROR(cda2dcmLogger, result.text() << ": writing file: " << encapsulator.getOutputFileName()); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - - OFLOG_INFO(cda2dcmLogger, "CDA encapsulation successful"); - return EXITCODE_NO_ERROR; + // clean up (Windows only, on Posix systems the stub will not return) + delete[] my_argv; + return result; } diff --git a/dcmdata/apps/dcm2cda.cc b/dcmdata/apps/dcm2cda.cc index 8a6f7ce5..25767c19 100644 --- a/dcmdata/apps/dcm2cda.cc +++ b/dcmdata/apps/dcm2cda.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2023-2024, OFFIS e.V. + * Copyright (C) 2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -13,332 +13,17 @@ * * Module: dcmdata * - * Author: Tingyan Xu + * Authors: Marco Eichelberg * - * Purpose: Extract CDA file from DICOM encapsulated CDA storage object + * Purpose: Proxy stub that calls dcmdecap * */ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/ofstd/ofstub.h" -#include "dcmtk/dcmdata/dctk.h" -#include "dcmtk/dcmdata/cmdlnarg.h" -#include "dcmtk/ofstd/ofconapp.h" -#include "dcmtk/dcmdata/dcuid.h" /* for dcmtk version name */ -#include "dcmtk/ofstd/ofstd.h" -#include "dcmtk/dcmdata/dcistrmz.h" /* for dcmZlibExpectRFC1950Encoding */ - -#ifdef WITH_ZLIB -#include /* for zlibVersion() */ -#endif - -#define OFFIS_CONSOLE_APPLICATION "dcm2cda" - -static OFLogger dcm2cdaLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION); - -static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" -OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; - -#define SHORTCOL 3 -#define LONGCOL 20 - -void addInputOptions(OFCommandLine& cmd) -{ - cmd.addGroup("input options:"); - cmd.addSubGroup("input file format:"); - cmd.addOption("--read-file", "+f", "read file format or data set (default)"); - cmd.addOption("--read-file-only", "+fo", "read file format only"); - cmd.addOption("--read-dataset", "-f", "read data set without file meta information"); - - cmd.addSubGroup("input transfer syntax:"); - cmd.addOption("--read-xfer-auto", "-t=", "use TS recognition (default)"); - cmd.addOption("--read-xfer-detect", "-td", "ignore TS specified in the file meta header"); - cmd.addOption("--read-xfer-little", "-te", "read with explicit VR little endian TS"); - cmd.addOption("--read-xfer-big", "-tb", "read with explicit VR big endian TS"); - cmd.addOption("--read-xfer-implicit", "-ti", "read with implicit VR little endian TS"); - - cmd.addSubGroup("parsing of odd-length attributes:"); - cmd.addOption("--accept-odd-length", "+ao", "accept odd length attributes (default)"); - cmd.addOption("--assume-even-length", "+ae", "assume real length is one byte larger"); - - cmd.addSubGroup("handling of undefined length UN elements:"); - cmd.addOption("--enable-cp246", "+ui", "read undefined len UN as implicit VR (default)"); - cmd.addOption("--disable-cp246", "-ui", "read undefined len UN as explicit VR"); - - cmd.addSubGroup("handling of defined length UN elements:"); - cmd.addOption("--retain-un", "-uc", "retain elements as UN (default)"); - cmd.addOption("--convert-un", "+uc", "convert to real VR if known"); - - cmd.addSubGroup("automatic data correction:"); - cmd.addOption("--enable-correction", "+dc", "enable automatic data correction (default)"); - cmd.addOption("--disable-correction", "-dc", "disable automatic data correction"); - -#ifdef WITH_ZLIB - cmd.addSubGroup("bitstream format of deflated input:"); - cmd.addOption("--bitstream-deflated", "+bd", "expect deflated bitstream (default)"); - cmd.addOption("--bitstream-zlib", "+bz", "expect deflated zlib bitstream"); -#endif -} - -void addDCM2CDACommandlineOptions(OFCommandLine& cmd) -{ - cmd.setOptionColumns(LONGCOL, SHORTCOL); - cmd.setParamColumn(LONGCOL + SHORTCOL + 4); - - cmd.addParam("dcmfile-in", "DICOM input filename (\"-\" for stdin)"); - cmd.addParam("cdafile-out", "CDA output filename"); - - cmd.addGeneralOptions(LONGCOL, SHORTCOL); - OFLog::addOptions(cmd); - addInputOptions(cmd); -} - -/** - * function for parsing commandline arguments - */ -void parseArguments(OFConsoleApplication& app, OFCommandLine& cmd, - E_FileReadMode& opt_readMode, E_TransferSyntax& opt_ixfer, - const char*& opt_ifname, - const char*& opt_ofname -) -{ - cmd.getParam(1, opt_ifname); - cmd.getParam(2, opt_ofname); - - OFLog::configureFromCommandLine(cmd, app); - - cmd.beginOptionBlock(); - if (cmd.findOption("--read-file")) - { - opt_readMode = ERM_autoDetect; - } - if (cmd.findOption("--read-file-only")) - { - opt_readMode = ERM_fileOnly; - } - if (cmd.findOption("--read-dataset")) - { - opt_readMode = ERM_dataset; - } - cmd.endOptionBlock(); - - cmd.beginOptionBlock(); - if (cmd.findOption("--read-xfer-auto")) - { - opt_ixfer = EXS_Unknown; - } - if (cmd.findOption("--read-xfer-detect")) - { - dcmAutoDetectDatasetXfer.set(OFTrue); - } - if (cmd.findOption("--read-xfer-little")) - { - app.checkDependence("--read-xfer-little", "--read-dataset", opt_readMode == ERM_dataset); - opt_ixfer = EXS_LittleEndianExplicit; - } - if (cmd.findOption("--read-xfer-big")) - { - app.checkDependence("--read-xfer-big", "--read-dataset", opt_readMode == ERM_dataset); - opt_ixfer = EXS_BigEndianExplicit; - } - if (cmd.findOption("--read-xfer-implicit")) - { - app.checkDependence("--read-xfer-implicit", "--read-dataset", opt_readMode == ERM_dataset); - opt_ixfer = EXS_LittleEndianImplicit; - } - cmd.endOptionBlock(); - - cmd.beginOptionBlock(); - if (cmd.findOption("--accept-odd-length")) - { - dcmAcceptOddAttributeLength.set(OFTrue); - } - if (cmd.findOption("--assume-even-length")) - { - dcmAcceptOddAttributeLength.set(OFFalse); - } - cmd.endOptionBlock(); - - cmd.beginOptionBlock(); - if (cmd.findOption("--enable-cp246")) - { - dcmEnableCP246Support.set(OFTrue); - } - if (cmd.findOption("--disable-cp246")) - { - dcmEnableCP246Support.set(OFFalse); - } - cmd.endOptionBlock(); - - cmd.beginOptionBlock(); - if (cmd.findOption("--retain-un")) - { - dcmEnableUnknownVRConversion.set(OFFalse); - } - if (cmd.findOption("--convert-un")) - { - dcmEnableUnknownVRConversion.set(OFTrue); - } - cmd.endOptionBlock(); - - cmd.beginOptionBlock(); - if (cmd.findOption("--enable-correction")) - { - dcmEnableAutomaticInputDataCorrection.set(OFTrue); - } - if (cmd.findOption("--disable-correction")) - { - dcmEnableAutomaticInputDataCorrection.set(OFFalse); - } - cmd.endOptionBlock(); - -#ifdef WITH_ZLIB - cmd.beginOptionBlock(); - if (cmd.findOption("--bitstream-deflated")) - { - dcmZlibExpectRFC1950Encoding.set(OFFalse); - } - if (cmd.findOption("--bitstream-zlib")) - { - dcmZlibExpectRFC1950Encoding.set(OFTrue); - } - cmd.endOptionBlock(); -#endif -} - -int main(int argc, char* argv[]) +int main(int argc, char** argv) { - const char* opt_ifname = NULL; - const char* opt_ofname = NULL; - E_FileReadMode opt_readMode = ERM_autoDetect; - E_TransferSyntax opt_ixfer = EXS_Unknown; - - OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Extract CDA file from DICOM encapsulated CDA", rcsid); - OFCommandLine cmd; - - // necessary options - addDCM2CDACommandlineOptions(cmd); - - // evaluating command line - prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); - if (app.parseCommandLine(cmd, argc, argv)) - { - // checking exclusive options first - if (cmd.hasExclusiveOption()) - { - if (cmd.findOption("--version")) - { - app.printHeader(OFTrue /*print host identifier*/); - COUT << OFendl << "External libraries used:"; -#ifdef WITH_ZLIB - COUT << OFendl << "- ZLIB, Version " << zlibVersion() << OFendl; -#else - COUT << " none" << OFendl; -#endif - return EXITCODE_NO_ERROR; - } - } - - /* command line parameters and options */ - parseArguments(app, cmd, opt_readMode, opt_ixfer, opt_ifname, opt_ofname); - } - - /* print resource identifier */ - OFLOG_DEBUG(dcm2cdaLogger, rcsid << OFendl); - - OFLOG_DEBUG(dcm2cdaLogger, "making sure data dictionary is loaded"); - if (!dcmDataDict.isDictionaryLoaded()) - { - OFLOG_WARN(dcm2cdaLogger, "no data dictionary loaded, check environment variable: " - << DCM_DICT_ENVIRONMENT_VARIABLE); - } - - // open input file - if ((opt_ifname == NULL) || (strlen(opt_ifname) == 0)) - { - OFLOG_ERROR(dcm2cdaLogger, "invalid filename: "); - return EXITCODE_NO_INPUT_FILES; - } - - OFCondition cond = EC_Normal; - - DcmFileFormat dfile; - DcmDataset* dataset = dfile.getDataset(); - - OFLOG_INFO(dcm2cdaLogger, "open input file " << opt_ifname); - /* load file to dfile, using given transfer syntax and other variables */ - cond = dfile.loadFile(opt_ifname, opt_ixfer, EGL_noChange, DCM_MaxReadLength, opt_readMode); - if (cond.bad()) - { - OFLOG_ERROR(dcm2cdaLogger, cond.text() << ": reading file: " << opt_ifname); - return EXITCODE_CANNOT_READ_INPUT_FILE; - } - - /* check SOP */ - OFString sopClass; - cond = dataset->findAndGetOFString(DCM_SOPClassUID, sopClass); - if (cond.bad() || sopClass != UID_EncapsulatedCDAStorage) - { - OFLOG_ERROR(dcm2cdaLogger, "SOPClassUID not of SOP UID_EncapsulatedCDAStorage: " << opt_ifname); - return EXITCODE_INVALID_INPUT_FILE; - } - - /* get EncapsulatedDocument */ - Uint8* cdaDocument = NULL; - unsigned long int delemlen = 0; - cond = dataset->findAndGetUint8Array(DCM_EncapsulatedDocument, (const Uint8*&)cdaDocument, &delemlen); - if (cond.bad() || cdaDocument == NULL || delemlen == 0) - { - OFLOG_ERROR(dcm2cdaLogger, "EncapsulatedDocument missing or has the wrong VR"); - return EXITCODE_INVALID_INPUT_FILE; - } - - /* get and check element Encapsulated Document Length */ - Uint32 lenElem; - cond = dataset->findAndGetUint32(DCM_EncapsulatedDocumentLength, lenElem); - /* EncapsulatedDocumentLength Element is invalid or - * it does not fit the length of the encapsulated document - * (it has to be equal or equal to EncapsulatedDocumentLength -1) - */ - if (cond.bad() || (lenElem != delemlen && lenElem != delemlen - 1)) - { - OFLOG_DEBUG(dcm2cdaLogger, "EncapsulatedDocumentLength missing or invalid, " - "using length of EncapsulatedDocument"); - lenElem = delemlen; - /* Strip pad byte at end of file, if there is one. - * CDA documents end with a closing XML tag, optionally followed by whitespace. - * If the last character of the file is not a CR ('\r', 13) or LF ('\n', 10), and not the - * letter '>', we assume it is either trailing garbage or a pad byte, and remove it. - */ - if (cdaDocument[lenElem - 1] != '\n' && cdaDocument[lenElem - 1] != '\r' && cdaDocument[lenElem - 1] != '>') - { - OFLOG_DEBUG(dcm2cdaLogger, "removing the pad byte at end of EncapsulatedDocument"); - --lenElem; - } - } - - OFLOG_INFO(dcm2cdaLogger, "writing CDA file to " << opt_ofname); - FILE* cdafile = fopen(opt_ofname, "wb"); - if (cdafile == NULL) - { - OFLOG_ERROR(dcm2cdaLogger, "unable to create file " << opt_ofname); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - - if (lenElem != fwrite(cdaDocument, 1, lenElem, cdafile)) - { - OFLOG_ERROR(dcm2cdaLogger, "write error in file " << opt_ofname); - fclose(cdafile); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - - if(fclose(cdafile)) - { - OFLOG_FATAL(dcm2cdaLogger, "write error in file " << opt_ofname); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - - OFLOG_INFO(dcm2cdaLogger, "conversion successful"); - - return EXITCODE_NO_ERROR; + // call stub. Will not return on Posix systems. + return OFstub_main(argc, argv, "dcm2cda", "dcmdecap"); } diff --git a/dcmdata/apps/dcm2json.cc b/dcmdata/apps/dcm2json.cc index 83be46ba..d4b4dc23 100644 --- a/dcmdata/apps/dcm2json.cc +++ b/dcmdata/apps/dcm2json.cc @@ -1,23 +1,23 @@ /* -* -* Copyright (C) 2016-2022, OFFIS e.V. -* All rights reserved. See COPYRIGHT file for details. -* -* This software and supporting documentation were developed by -* -* OFFIS e.V. -* R&D Division Health -* Escherweg 2 -* D-26121 Oldenburg, Germany -* -* -* Module: dcmdata -* -* Author: Sebastian Grallert -* -* Purpose: Convert the contents of a DICOM file to JSON format -* -*/ + * + * Copyright (C) 2016-2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmdata + * + * Author: Sebastian Grallert, Marco Eichelberg + * + * Purpose: Convert the contents of a DICOM file to JSON format + * + */ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ @@ -28,6 +28,9 @@ #include "dcmtk/ofstd/ofstream.h" #include "dcmtk/ofstd/ofconapp.h" #include "dcmtk/ofstd/ofexit.h" +#include "dcmtk/ofstd/ofstd.h" + +#include #ifdef WITH_ZLIB #include /* for zlibVersion() */ @@ -35,6 +38,9 @@ #ifdef DCMTK_ENABLE_CHARSET_CONVERSION #include "dcmtk/ofstd/ofchrenc.h" /* for OFCharacterEncoding */ #endif +#ifdef HAVE_WINDOWS_H +#include +#endif #define OFFIS_CONSOLE_APPLICATION "dcm2json" #define OFFIS_CONSOLE_DESCRIPTION "Convert DICOM file and data set to JSON" @@ -42,6 +48,8 @@ #define EXITCODE_CANNOT_CONVERT_TO_UNICODE 80 #define EXITCODE_CANNOT_WRITE_VALID_JSON 81 +#define DCM2JSON_WELL_KNOWN_UID "1.2.276.0.7230010.3.1.4.1787205428.3192777.1748004619.679033" + static OFLogger dcm2jsonLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION); static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" @@ -49,18 +57,36 @@ OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; // ******************************************** -/* Function to call all writeJson() functions in DCMTK */ -static OFCondition writeFile(STD_NAMESPACE ostream &out, - const char *ifname, +/** Convert a DICOM file to JSON. + * @param out output stream to write JSON to + * @param dfile pointer to the DICOM object, must not be NULL + * @param readMode mode used to read the file. Determines whether conversion + * takes place at DcmFileFormat or DcmDataset level. + * @param format OFTrue for pretty printing, OFFalse for compact code + * @param printMetaInfo If true, the meta-header attributes are also converted + * to JSON as a non-standard DCMTK extension + * @param encode_extended enables an extension of the JSON syntax to permit + * "-inf", "nan" and "inf" to be generated for infinity and not-a-number values + * @param opt_ns_policy policy for converting IS/DS values to JSON + * @param min_bulk_size minimum size for bulk data, negative number to disable bulk data + * @param bulk_uri_prefix prefix string for bulk data URIs + * @param bulk_dir directory to which bulk data should be written + * @return EC_Normal if successful, and error code otherwise + */ +static OFCondition writeFile( + STD_NAMESPACE ostream &out, DcmFileFormat *dfile, const E_FileReadMode readMode, const OFBool format, const OFBool printMetaInfo, const OFBool encode_extended, - const DcmJsonFormat::NumStringPolicy opt_ns_policy) + const DcmJsonFormat::NumStringPolicy opt_ns_policy, + const OFCmdSignedInt min_bulk_size, + const char *bulk_uri_prefix, + const char *bulk_dir) { OFCondition result = EC_IllegalParameter; - if ((ifname != NULL) && (dfile != NULL)) + if (dfile != NULL) { /* write JSON document content */ DcmDataset *dset = dfile->getDataset(); @@ -69,6 +95,9 @@ static OFCondition writeFile(STD_NAMESPACE ostream &out, DcmJsonFormatPretty fmt(printMetaInfo); fmt.setJsonExtensionEnabled(encode_extended); fmt.setJsonNumStringPolicy(opt_ns_policy); + fmt.setMinBulkSize(OFstatic_cast(ssize_t, min_bulk_size)); + fmt.setBulkURIPrefix(bulk_uri_prefix); + fmt.setBulkDir(bulk_dir); if (readMode == ERM_dataset) result = dset->writeJsonExt(out, fmt, OFTrue, OFTrue); else result = dfile->writeJson(out, fmt); @@ -78,6 +107,9 @@ static OFCondition writeFile(STD_NAMESPACE ostream &out, DcmJsonFormatCompact fmt(printMetaInfo); fmt.setJsonExtensionEnabled(encode_extended); fmt.setJsonNumStringPolicy(opt_ns_policy); + fmt.setMinBulkSize(OFstatic_cast(ssize_t, min_bulk_size)); + fmt.setBulkURIPrefix(bulk_uri_prefix); + fmt.setBulkDir(bulk_dir); if (readMode == ERM_dataset) result = dset->writeJsonExt(out, fmt, OFTrue, OFTrue); else result = dfile->writeJson(out, fmt); @@ -86,6 +118,67 @@ static OFCondition writeFile(STD_NAMESPACE ostream &out, return result; } +/** append the given file path to the output URL while URL-encoding + * special characters. Note that the reserved characters '/' and + * ':' are not encoding since they are routinely used in file: URLs. + * @param path file path + * @param output_url output URL + */ +static void appendURLEncodedPath(const char *path, OFString& output_url) +{ + if (path) + { + char c; + for (const char *p=path; *p != '\0'; ++p) + { + c = *p; + // URL encode all characters except a-z, A-Z, 0-9, and "-_./!~$:" + // The reserved characters "/" and ":" are not URL encoded because they routinely occur in file: URLs + if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '/' || c == '!' || c == '~' || c == '$' || c == ':') output_url.append(1, *p); + else if (c == '\\') + { + // convert backslashes to forward slashes + output_url.append("/"); + } + else + { + char buf[5]; + OFStandard::snprintf(buf, 5, "%%%02X", c); + output_url.append(buf); + } + } + } +} + +/** determine the real path to the given working directory + * and return it in the form of a file:// URI. + * @param input_dir current working directory, NULL for current directory + * @param output_dir file URI to real path (without symbolic links) returned in this parameter if successful + * @return EC_Normal if successful, and error code otherwise + */ +static OFCondition getCurrentWorkingDir(const char *input_dir, OFString& output_dir) +{ + // if dir is empty, assume current directory + if (input_dir == NULL) input_dir = "."; + +#ifdef HAVE_WINDOWS_H + char *resolved_path = _fullpath(NULL, input_dir, 0); +#else + char *resolved_path = realpath(input_dir, NULL); +#endif + if (resolved_path == NULL) + { + OFLOG_ERROR(dcm2jsonLogger, OFFIS_CONSOLE_APPLICATION << ": Cannot create or determine bulk data directory"); + return EC_DirectoryDoesNotExist; + } + output_dir = "file://localhost"; + if (resolved_path[0] != '/') output_dir.append("/"); + appendURLEncodedPath(resolved_path, output_dir); + output_dir.append("/"); + free(resolved_path); + return EC_Normal; +} + #define SHORTCOL 3 #define LONGCOL 20 @@ -100,6 +193,12 @@ int main(int argc, char *argv[]) E_TransferSyntax opt_ixfer = EXS_Unknown; OFString optStr; + OFCmdSignedInt opt_min_bulk_size = -1; + const char *opt_bulk_uri_prefix = NULL; + const char *opt_bulk_dir = "."; + OFBool opt_bulk_subdir = OFFalse; + OFString bulkURIPrefix; + OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, OFFIS_CONSOLE_DESCRIPTION, rcsid); OFCommandLine cmd; cmd.setOptionColumns(LONGCOL, SHORTCOL); @@ -115,30 +214,37 @@ int main(int argc, char *argv[]) cmd.addGroup("input options:"); cmd.addSubGroup("input file format:"); - cmd.addOption("--read-file", "+f", "read file format or data set (default)"); - cmd.addOption("--read-file-only", "+fo", "read file format only"); - cmd.addOption("--read-dataset", "-f", "read data set without file meta information"); + cmd.addOption("--read-file", "+f", "read file format or data set (default)"); + cmd.addOption("--read-file-only", "+fo", "read file format only"); + cmd.addOption("--read-dataset", "-f", "read data set without file meta information"); cmd.addSubGroup("input transfer syntax:"); - cmd.addOption("--read-xfer-auto", "-t=", "use TS recognition (default)"); - cmd.addOption("--read-xfer-detect", "-td", "ignore TS specified in the file meta header"); - cmd.addOption("--read-xfer-little", "-te", "read with explicit VR little endian TS"); - cmd.addOption("--read-xfer-big", "-tb", "read with explicit VR big endian TS"); - cmd.addOption("--read-xfer-implicit", "-ti", "read with implicit VR little endian TS"); + cmd.addOption("--read-xfer-auto", "-t=", "use TS recognition (default)"); + cmd.addOption("--read-xfer-detect", "-td", "ignore TS specified in the file meta header"); + cmd.addOption("--read-xfer-little", "-te", "read with explicit VR little endian TS"); + cmd.addOption("--read-xfer-big", "-tb", "read with explicit VR big endian TS"); + cmd.addOption("--read-xfer-implicit", "-ti", "read with implicit VR little endian TS"); cmd.addGroup("processing options:"); cmd.addSubGroup("encoding of infinity and not-a-number:"); - cmd.addOption("--encode-strict", "-es", "report error for 'inf' and 'nan' (default)"); - cmd.addOption("--encode-extended", "-ee", "permit 'inf' and 'nan' in JSON numbers"); + cmd.addOption("--encode-strict", "-es", "report error for 'inf' and 'nan' (default)"); + cmd.addOption("--encode-extended", "-ee", "permit 'inf' and 'nan' in JSON numbers"); cmd.addSubGroup("encoding of IS and DS (integer/decimal string) elements:"); - cmd.addOption("--is-ds-auto", "-ia", "encode as number if valid, as string\notherwise (default)"); - cmd.addOption("--is-ds-num", "-in", "always encode as number, fail if invalid"); - cmd.addOption("--is-ds-string", "-is", "always encode as string"); + cmd.addOption("--is-ds-auto", "-ia", "encode as number if valid, as string\notherwise (default)"); + cmd.addOption("--is-ds-num", "-in", "always encode as number, fail if invalid"); + cmd.addOption("--is-ds-string", "-is", "always encode as string"); + cmd.addSubGroup("bulk data URI options:"); + cmd.addOption("--bulk-disabled", "-b", "write everything as inline binary (default)"); + cmd.addOption("--bulk-enabled", "+b", "write large attributes as bulk data"); + cmd.addOption("--bulk-size", "+bz", 1, "[s]ize: integer (default: 1)", "use bulk data for attributes >= s kBytes"); + cmd.addOption("--bulk-uri-prefix", "+bp", 1, "[u]ri prefix: string", "use prefix u when generating bulk data URIs\n(default: file URI)"); + cmd.addOption("--bulk-dir", "+bd", 1, "[d]irectory: string", "write bulk data files to d (default: '.')"); + cmd.addOption("--bulk-subdir", "+bs", "create subdirectory for each SOP instance\n(default: no subdirectory)"); cmd.addGroup("output options:"); cmd.addSubGroup("output format:"); - cmd.addOption("--formatted-code", "+fc", "enable whitespace formatting (default)"); - cmd.addOption("--compact-code", "-fc", "print only required characters"); - cmd.addOption("--write-meta", "+m", "write data set with meta information\n(warning: not conforming to the DICOM standard)"); + cmd.addOption("--formatted-code", "+fc", "enable whitespace formatting (default)"); + cmd.addOption("--compact-code", "-fc", "print only required characters"); + cmd.addOption("--write-meta", "+m", "write data set with meta information\n(warning: not conforming to the DICOM standard)"); /* evaluate command line */ prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); @@ -215,6 +321,37 @@ int main(int argc, char *argv[]) opt_ns_policy = DcmJsonFormat::NSP_always_string; cmd.endOptionBlock(); + cmd.beginOptionBlock(); + if (cmd.findOption("--bulk-disabled")) + opt_min_bulk_size = -1; + if (cmd.findOption("--bulk-enabled")) + opt_min_bulk_size = 1; + cmd.endOptionBlock(); + + if (cmd.findOption("--bulk-size")) + { + app.checkDependence("--bulk-size", "--bulk-enabled", opt_min_bulk_size >= 0); + app.checkValue(cmd.getValueAndCheckMin(opt_min_bulk_size, 0)); + } + + if (cmd.findOption("--bulk-uri-prefix")) + { + app.checkDependence("--bulk-uri-prefix", "--bulk-enabled", opt_min_bulk_size >= 0); + app.checkValue(cmd.getValue(opt_bulk_uri_prefix)); + } + + if (cmd.findOption("--bulk-dir")) + { + app.checkDependence("--bulk-dir", "--bulk-enabled", opt_min_bulk_size >= 0); + app.checkValue(cmd.getValue(opt_bulk_dir)); + } + + if (cmd.findOption("--bulk-subdir")) + { + app.checkDependence("--bulk-subdir", "--bulk-enabled", opt_min_bulk_size >= 0); + opt_bulk_subdir = OFTrue; + } + /* format options */ cmd.beginOptionBlock(); if (cmd.findOption("--formatted-code")) @@ -241,67 +378,89 @@ int main(int argc, char *argv[]) << DCM_DICT_ENVIRONMENT_VARIABLE); } + OFCondition status; + if ((opt_min_bulk_size >= 0) && (opt_bulk_uri_prefix == NULL)) + { + // try to create bulk directory, ignore failure + (void) OFStandard::createDirectory(opt_bulk_dir, ""); + + // determine default Bulk URI prefix + status = getCurrentWorkingDir(opt_bulk_dir, bulkURIPrefix); + if (status.bad()) + { + return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; + } + opt_bulk_uri_prefix = bulkURIPrefix.c_str(); + } + int result = 0; /* first parameter is treated as the input filename */ const char *ifname = NULL; cmd.getParam(1, ifname); + /* check input file */ if ((ifname != NULL) && (strlen(ifname) > 0)) { /* read DICOM file or data set */ DcmFileFormat dfile; - OFCondition status = dfile.loadFile(ifname, opt_ixfer, EGL_noChange, DCM_MaxReadLength, opt_readMode); + status = dfile.loadFile(ifname, opt_ixfer, EGL_noChange, DCM_MaxReadLength, opt_readMode); if (status.good()) { DcmDataset *dset = dfile.getDataset(); - OFString csetString; - if (dset->findAndGetOFStringArray(DCM_SpecificCharacterSet, csetString).good()) + /* check for SpecificCharacterSet on any data set level */ + if (dset->tagExistsWithValue(DCM_SpecificCharacterSet, OFTrue /*searchIntoSub*/)) { - if (csetString.compare("ISO_IR 6") == 0) + /* convert entire DICOM file or data set to UTF-8 encoding */ + status = dfile.convertToUTF8(); + if (status.bad()) { - /* SpecificCharacterSet indicates ASCII without extended characters. - * If this is true, no conversion is necessary. Check for extended characters. - */ - if (dset->containsExtendedCharacters(OFFalse /*checkAllStrings*/)) - { - OFLOG_FATAL(dcm2jsonLogger, "dataset contains extended characters but SpecificCharacterSet (0008,0005) is 'ISO_IR 6'"); - result = EXITCODE_CANNOT_CONVERT_TO_UNICODE; - } - } - else if (csetString.compare("ISO_IR 192") == 0) - { - /* DICOM dataset is already in UTF-8, no conversion necessary */ - } - else - { - /* we have a character set other than ASCII or UTF-8. Perform conversion. */ -#ifdef DCMTK_ENABLE_CHARSET_CONVERSION - /* convert all DICOM strings to UTF-8 */ - OFLOG_INFO(dcm2jsonLogger, "converting all element values that are affected by SpecificCharacterSet (0008,0005) to UTF-8"); - status = dset->convertToUTF8(); - if (status.bad()) - { - OFLOG_FATAL(dcm2jsonLogger, status.text() << ": converting file to UTF-8: " << ifname); - result = EXITCODE_CANNOT_CONVERT_TO_UNICODE; - } -#else - OFLOG_FATAL(dcm2jsonLogger, "character set conversion not available"); + OFLOG_FATAL(dcm2jsonLogger, status.text() << ": converting file to UTF-8: " << ifname); result = EXITCODE_CANNOT_CONVERT_TO_UNICODE; -#endif } } else { - /* SpecificCharacterSet not present */ - if (dset->containsExtendedCharacters(OFFalse /*checkAllStrings*/)) - { - OFLOG_FATAL(dcm2jsonLogger, "dataset contains extended characters but no SpecificCharacterSet (0008,0005)"); - result = EXITCODE_CANNOT_CONVERT_TO_UNICODE; - } + if (dset->containsExtendedCharacters(OFFalse /*checkAllStrings*/)) + { + OFLOG_ERROR(dcm2jsonLogger, OFFIS_CONSOLE_APPLICATION << ": SpecificCharacterSet (0008,0005) element " + << "absent (at all levels of the data set) but extended characters used in file: " << ifname); + result = EXITCODE_CANNOT_CONVERT_TO_UNICODE; + } } if (result == 0) { + + // look for the SOP instance UID, first in the dataset, then in the metaheader + OFString subDir; + OFString bulkDir = opt_bulk_dir; + OFString bulkURIPrefixWithSubdir; + if (opt_bulk_uri_prefix) bulkURIPrefixWithSubdir = opt_bulk_uri_prefix; + if ((opt_min_bulk_size >= 0) && opt_bulk_subdir) + { + if (dfile.getDataset()->findAndGetOFString(DCM_SOPInstanceUID, subDir).bad() + && dfile.getMetaInfo()->findAndGetOFString(DCM_SOPInstanceUID, subDir).bad()) + { + // no SOP instance UID found. Use hard coded default UID instead. + subDir = DCM2JSON_WELL_KNOWN_UID; + } + + bulkURIPrefixWithSubdir.append(subDir); + bulkURIPrefixWithSubdir.append("/"); + + bulkDir.append(1, PATH_SEPARATOR); + bulkDir.append(subDir); + if (! OFStandard::dirExists(bulkDir)) + { + status = OFStandard::createDirectory(bulkDir, opt_bulk_dir); + if (status.bad()) + { + OFLOG_FATAL(dcm2jsonLogger, status.text() << ": " << ifname); + return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; + } + } + } + /* if second parameter is present, it is treated as the output filename ("stdout" otherwise) */ if (cmd.getParamCount() == 2) { @@ -311,7 +470,8 @@ int main(int argc, char *argv[]) if (stream.good()) { /* write content in JSON format to file */ - status = writeFile(stream, ifname, &dfile, opt_readMode, opt_format, opt_addMetaInformation, opt_encode_extended, opt_ns_policy); + status = writeFile(stream, &dfile, opt_readMode, opt_format, opt_addMetaInformation, + opt_encode_extended, opt_ns_policy, opt_min_bulk_size, bulkURIPrefixWithSubdir.c_str(), bulkDir.c_str()); if (status.bad()) { OFLOG_FATAL(dcm2jsonLogger, status.text() << ": " << ifname); @@ -324,7 +484,8 @@ int main(int argc, char *argv[]) else { /* write content in JSON format to standard output */ - status = writeFile(COUT, ifname, &dfile, opt_readMode, opt_format, opt_addMetaInformation, opt_encode_extended, opt_ns_policy); + status = writeFile(COUT, &dfile, opt_readMode, opt_format, opt_addMetaInformation, + opt_encode_extended, opt_ns_policy, opt_min_bulk_size, bulkURIPrefixWithSubdir.c_str(), bulkDir.c_str()); if (status.bad()) { OFLOG_FATAL(dcm2jsonLogger, status.text() << ": " << ifname); diff --git a/dcmdata/apps/dcm2pdf.cc b/dcmdata/apps/dcm2pdf.cc index f00de6e3..08a1ed04 100644 --- a/dcmdata/apps/dcm2pdf.cc +++ b/dcmdata/apps/dcm2pdf.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2007-2024, OFFIS e.V. + * Copyright (C) 2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -13,344 +13,17 @@ * * Module: dcmdata * - * Author: Marco Eichelberg + * Authors: Marco Eichelberg * - * Purpose: Extract PDF file from DICOM encapsulated PDF storage object + * Purpose: Proxy stub that calls dcmdecap * */ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/ofstd/ofstub.h" -BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H -#include /* for O_RDONLY */ -#endif -#ifdef HAVE_SYS_TYPES_H -#include /* required for sys/stat.h */ -#endif -#ifdef HAVE_SYS_STAT_H -#include /* for stat, fstat */ -#endif -END_EXTERN_C - -#include "dcmtk/dcmdata/dctk.h" -#include "dcmtk/dcmdata/cmdlnarg.h" -#include "dcmtk/ofstd/ofconapp.h" -#include "dcmtk/dcmdata/dcuid.h" /* for dcmtk version name */ -#include "dcmtk/ofstd/ofstd.h" -#include "dcmtk/dcmdata/dcistrmz.h" /* for dcmZlibExpectRFC1950Encoding */ - -#ifdef WITH_ZLIB -#include /* for zlibVersion() */ -#endif - -#define OFFIS_CONSOLE_APPLICATION "dcm2pdf" - -static OFLogger dcm2pdfLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION); - -static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" - OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; - -#define FILENAME_PLACEHOLDER "#f" - -static OFString replaceChars(const OFString &srcstr, const OFString &pattern, const OFString &substitute) -/* - * This function replaces all occurrences of pattern in srcstr with substitute and returns - * the result as a new OFString variable. Note that srcstr itself will not be changed. - * - * Parameters: - * srcstr - [in] The source string. - * pattern - [in] The pattern string which shall be substituted. - * substitute - [in] The substitute for pattern in srcstr. - */ -{ - OFString result = srcstr; - size_t pos = 0; - - while (pos != OFString_npos) - { - pos = result.find(pattern, pos); - - if (pos != OFString_npos) - { - result.replace(pos, pattern.size(), substitute); - pos += substitute.size(); - } - } - - return result; -} - - -#define SHORTCOL 3 -#define LONGCOL 20 - -int main(int argc, char *argv[]) +int main(int argc, char** argv) { - const char *opt_ifname = NULL; - const char *opt_ofname = NULL; - const char *opt_execString = NULL; - E_FileReadMode opt_readMode = ERM_autoDetect; - E_TransferSyntax opt_ixfer = EXS_Unknown; - - OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Extract PDF file from DICOM encapsulated PDF", rcsid); - OFCommandLine cmd; - cmd.setOptionColumns(LONGCOL, SHORTCOL); - cmd.setParamColumn(LONGCOL + SHORTCOL + 4); - - cmd.addParam("dcmfile-in", "DICOM input filename (\"-\" for stdin)"); - cmd.addParam("pdffile-out", "PDF output filename"); - - cmd.addGroup("general options:", LONGCOL, SHORTCOL + 2); - cmd.addOption("--help", "-h", "print this help text and exit", OFCommandLine::AF_Exclusive); - cmd.addOption("--version", "print version information and exit", OFCommandLine::AF_Exclusive); - OFLog::addOptions(cmd); - - cmd.addGroup("input options:"); - cmd.addSubGroup("input file format:"); - cmd.addOption("--read-file", "+f", "read file format or data set (default)"); - cmd.addOption("--read-file-only", "+fo", "read file format only"); - cmd.addOption("--read-dataset", "-f", "read data set without file meta information"); - cmd.addSubGroup("input transfer syntax:"); - cmd.addOption("--read-xfer-auto", "-t=", "use TS recognition (default)"); - cmd.addOption("--read-xfer-detect", "-td", "ignore TS specified in the file meta header"); - cmd.addOption("--read-xfer-little", "-te", "read with explicit VR little endian TS"); - cmd.addOption("--read-xfer-big", "-tb", "read with explicit VR big endian TS"); - cmd.addOption("--read-xfer-implicit", "-ti", "read with implicit VR little endian TS"); - cmd.addSubGroup("parsing of odd-length attributes:"); - cmd.addOption("--accept-odd-length", "+ao", "accept odd length attributes (default)"); - cmd.addOption("--assume-even-length", "+ae", "assume real length is one byte larger"); - cmd.addSubGroup("handling of undefined length UN elements:"); - cmd.addOption("--enable-cp246", "+ui", "read undefined len UN as implicit VR (default)"); - cmd.addOption("--disable-cp246", "-ui", "read undefined len UN as explicit VR"); - cmd.addSubGroup("handling of defined length UN elements:"); - cmd.addOption("--retain-un", "-uc", "retain elements as UN (default)"); - cmd.addOption("--convert-un", "+uc", "convert to real VR if known"); - cmd.addSubGroup("automatic data correction:"); - cmd.addOption("--enable-correction", "+dc", "enable automatic data correction (default)"); - cmd.addOption("--disable-correction", "-dc", "disable automatic data correction"); -#ifdef WITH_ZLIB - cmd.addSubGroup("bitstream format of deflated input:"); - cmd.addOption("--bitstream-deflated", "+bd", "expect deflated bitstream (default)"); - cmd.addOption("--bitstream-zlib", "+bz", "expect deflated zlib bitstream"); -#endif - - cmd.addGroup("processing options:"); - cmd.addSubGroup("execution options:"); - cmd.addOption("--exec", "-x", 1, "[c]ommand: string", - "execute command c after PDF extraction"); - /* evaluate command line */ - prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); - if (app.parseCommandLine(cmd, argc, argv)) - { - /* check exclusive options first */ - if (cmd.hasExclusiveOption()) - { - if (cmd.findOption("--version")) - { - app.printHeader(OFTrue /*print host identifier*/); - COUT << OFendl << "External libraries used:"; -#ifdef WITH_ZLIB - COUT << OFendl << "- ZLIB, Version " << zlibVersion() << OFendl; -#else - COUT << " none" << OFendl; -#endif - return EXITCODE_NO_ERROR; - } - } - - /* command line parameters and options */ - cmd.getParam(1, opt_ifname); - cmd.getParam(2, opt_ofname); - - OFLog::configureFromCommandLine(cmd, app); - - cmd.beginOptionBlock(); - if (cmd.findOption("--read-file")) opt_readMode = ERM_autoDetect; - if (cmd.findOption("--read-file-only")) opt_readMode = ERM_fileOnly; - if (cmd.findOption("--read-dataset")) opt_readMode = ERM_dataset; - cmd.endOptionBlock(); - - cmd.beginOptionBlock(); - if (cmd.findOption("--read-xfer-auto")) - opt_ixfer = EXS_Unknown; - if (cmd.findOption("--read-xfer-detect")) - dcmAutoDetectDatasetXfer.set(OFTrue); - if (cmd.findOption("--read-xfer-little")) - { - app.checkDependence("--read-xfer-little", "--read-dataset", opt_readMode == ERM_dataset); - opt_ixfer = EXS_LittleEndianExplicit; - } - if (cmd.findOption("--read-xfer-big")) - { - app.checkDependence("--read-xfer-big", "--read-dataset", opt_readMode == ERM_dataset); - opt_ixfer = EXS_BigEndianExplicit; - } - if (cmd.findOption("--read-xfer-implicit")) - { - app.checkDependence("--read-xfer-implicit", "--read-dataset", opt_readMode == ERM_dataset); - opt_ixfer = EXS_LittleEndianImplicit; - } - cmd.endOptionBlock(); - - cmd.beginOptionBlock(); - if (cmd.findOption("--accept-odd-length")) - { - dcmAcceptOddAttributeLength.set(OFTrue); - } - if (cmd.findOption("--assume-even-length")) - { - dcmAcceptOddAttributeLength.set(OFFalse); - } - cmd.endOptionBlock(); - - cmd.beginOptionBlock(); - if (cmd.findOption("--enable-cp246")) - { - dcmEnableCP246Support.set(OFTrue); - } - if (cmd.findOption("--disable-cp246")) - { - dcmEnableCP246Support.set(OFFalse); - } - cmd.endOptionBlock(); - - cmd.beginOptionBlock(); - if (cmd.findOption("--retain-un")) - { - dcmEnableUnknownVRConversion.set(OFFalse); - } - if (cmd.findOption("--convert-un")) - { - dcmEnableUnknownVRConversion.set(OFTrue); - } - cmd.endOptionBlock(); - - cmd.beginOptionBlock(); - if (cmd.findOption("--enable-correction")) - { - dcmEnableAutomaticInputDataCorrection.set(OFTrue); - } - if (cmd.findOption("--disable-correction")) - { - dcmEnableAutomaticInputDataCorrection.set(OFFalse); - } - cmd.endOptionBlock(); - -#ifdef WITH_ZLIB - cmd.beginOptionBlock(); - if (cmd.findOption("--bitstream-deflated")) - { - dcmZlibExpectRFC1950Encoding.set(OFFalse); - } - if (cmd.findOption("--bitstream-zlib")) - { - dcmZlibExpectRFC1950Encoding.set(OFTrue); - } - cmd.endOptionBlock(); -#endif - - if (cmd.findOption("--exec")) - app.checkValue(cmd.getValue(opt_execString)); - } - - /* print resource identifier */ - OFLOG_DEBUG(dcm2pdfLogger, rcsid << OFendl); - - /* make sure data dictionary is loaded */ - if (!dcmDataDict.isDictionaryLoaded()) - { - OFLOG_WARN(dcm2pdfLogger, "no data dictionary loaded, check environment variable: " - << DCM_DICT_ENVIRONMENT_VARIABLE); - } - - // open inputfile - if ((opt_ifname == NULL) || (strlen(opt_ifname) == 0)) - { - OFLOG_FATAL(dcm2pdfLogger, "invalid filename: "); - return EXITCODE_NO_INPUT_FILES; - } - - DcmFileFormat fileformat; - DcmDataset * dataset = fileformat.getDataset(); - - OFLOG_INFO(dcm2pdfLogger, "open input file " << opt_ifname); - - OFCondition error = fileformat.loadFile(opt_ifname, opt_ixfer, EGL_noChange, DCM_MaxReadLength, opt_readMode); - - if (error.bad()) - { - OFLOG_FATAL(dcm2pdfLogger, error.text() << ": reading file: " << opt_ifname); - return EXITCODE_CANNOT_READ_INPUT_FILE; - } - - OFString sopClass; - error = dataset->findAndGetOFString(DCM_SOPClassUID, sopClass); - if (error.bad() || sopClass != UID_EncapsulatedPDFStorage) - { - OFLOG_FATAL(dcm2pdfLogger, "not an Encapsulated PDF object: " << opt_ifname); - return EXITCODE_INVALID_INPUT_FILE; - } - - DcmElement *delem = NULL; - error = dataset->findAndGetElement(DCM_EncapsulatedDocument, delem); - if (error.bad() || delem == NULL) - { - OFLOG_FATAL(dcm2pdfLogger, "Encapsulated Document missing."); - return EXITCODE_INVALID_INPUT_FILE; - } - - Uint32 len = delem->getLength(); - Uint8 *pdfDocument = NULL; - error = delem->getUint8Array(pdfDocument); - if (error.bad() || pdfDocument == NULL || len == 0) - { - OFLOG_FATAL(dcm2pdfLogger, "Encapsulated Document empty or wrong VR."); - return EXITCODE_INVALID_INPUT_FILE; - } - - /* strip pad byte at end of file, if there is one. The PDF format expects - * files to end with %%EOF followed by CR/LF (although in some cases the - * CR/LF may be missing or you might only find CR or LF). - * If the last character of the file is not a CR or LF, and not the - * letter 'F', we assume it is either trailing garbage or a pad byte, and remove it. - */ - if (pdfDocument[len-1] != 10 && pdfDocument[len-1] != 13 && pdfDocument[len-1] != 'F') - { - --len; - } - - OFLOG_INFO(dcm2pdfLogger, "writing PDF file to " << opt_ofname); - FILE *pdffile = fopen(opt_ofname, "wb"); - if (pdffile == NULL) - { - OFLOG_FATAL(dcm2pdfLogger, "unable to create file " << opt_ofname); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - - if (len != fwrite(pdfDocument, 1, len, pdffile)) - { - OFLOG_FATAL(dcm2pdfLogger, "write error in file " << opt_ofname); - fclose(pdffile); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - - if(fclose(pdffile)) - { - OFLOG_FATAL(dcm2pdfLogger, "write error in file " << opt_ofname); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - - OFLOG_INFO(dcm2pdfLogger, "conversion successful"); - - if (opt_execString) - { - OFString cmdStr = opt_execString; - cmdStr = replaceChars(cmdStr, OFString(FILENAME_PLACEHOLDER), opt_ofname); - - // Execute command and return result - return system(cmdStr.c_str()); - } - - return EXITCODE_NO_ERROR; + // call stub. Will not return on Posix systems. + return OFstub_main(argc, argv, "dcm2pdf", "dcmdecap"); } diff --git a/dcmdata/apps/dcm2xml.cc b/dcmdata/apps/dcm2xml.cc index 61181284..a2b41a3d 100644 --- a/dcmdata/apps/dcm2xml.cc +++ b/dcmdata/apps/dcm2xml.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2022, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -105,13 +105,32 @@ static OFCondition checkCharacterSet(const char *ifname, /* SpecificCharacterSet is not present in the dataset */ if (dset->containsExtendedCharacters(checkAllStrings)) { + OFString sopClass; + /* check whether this file is a DICOMDIR */ + const OFBool isDICOMDIR = dfile.getMetaInfo()->findAndGetOFString(DCM_MediaStorageSOPClassUID, sopClass).good() && + (sopClass == UID_MediaStorageDirectoryStorage); if (defaultCharset == NULL) { - /* the dataset contains non-ASCII characters that really should not be there */ - OFLOG_ERROR(dcm2xmlLogger, OFFIS_CONSOLE_APPLICATION << ": SpecificCharacterSet (0008,0005) " - << "element absent (on the main data set level) but extended characters used in file: " << ifname); - OFLOG_DEBUG(dcm2xmlLogger, "use option --charset-assume to manually specify an appropriate character set"); - result = makeOFCondition(OFM_dcmdata, EC_CODE_CannotSelectCharacterSet, OF_error, "Missing Specific Character Set");; + if (isDICOMDIR) + { + /* check for SpecificCharacterSet on any data set level */ + if (dset->tagExistsWithValue(DCM_SpecificCharacterSet, OFTrue /*searchIntoSub*/)) + { + /* for now, we assume that everything is ok */ + result = EC_Normal; + } else { + OFLOG_ERROR(dcm2xmlLogger, OFFIS_CONSOLE_APPLICATION << ": SpecificCharacterSet (0008,0005) element " + << "absent (at all levels of the data set) but extended characters used in file: " << ifname); + OFLOG_DEBUG(dcm2xmlLogger, "try using option --charset-assume to manually specify an appropriate character set"); + result = makeOFCondition(OFM_dcmdata, EC_CODE_CannotSelectCharacterSet, OF_error, "Missing Specific Character Set"); + } + } else { + /* the dataset contains non-ASCII characters that really should not be there */ + OFLOG_ERROR(dcm2xmlLogger, OFFIS_CONSOLE_APPLICATION << ": SpecificCharacterSet (0008,0005) element " + << "absent (at the main level of the data set) but extended characters used in file: " << ifname); + OFLOG_DEBUG(dcm2xmlLogger, "use option --charset-assume to manually specify an appropriate character set"); + result = makeOFCondition(OFM_dcmdata, EC_CODE_CannotSelectCharacterSet, OF_error, "Missing Specific Character Set"); + } } else { result = EC_Normal; csetString = defaultCharset; @@ -164,17 +183,11 @@ static OFCondition checkCharacterSet(const char *ifname, << defaultCharset << "' specified with option --charset-assume not supported"); result = makeOFCondition(OFM_dcmdata, EC_CODE_CannotSelectCharacterSet, OF_error, "Cannot select character set"); } - if (result.good()) + if (result.good() && !isDICOMDIR) { - OFString sopClass; - /* check whether this file is a DICOMDIR */ - if (dfile.getMetaInfo()->findAndGetOFString(DCM_MediaStorageSOPClassUID, sopClass).bad() || - (sopClass != UID_MediaStorageDirectoryStorage)) - { - OFLOG_INFO(dcm2xmlLogger, "inserting SpecificCharacterSet (0008,0005) element with value '" << csetString << "'"); - /* insert the SpecificCharacterSet (0008,0005) element with new value */ - result = dset->putAndInsertOFStringArray(DCM_SpecificCharacterSet, csetString); - } + OFLOG_INFO(dcm2xmlLogger, "inserting SpecificCharacterSet (0008,0005) element with value '" << csetString << "'"); + /* insert the SpecificCharacterSet (0008,0005) element with new value */ + result = dset->putAndInsertOFStringArray(DCM_SpecificCharacterSet, csetString); } } } else { @@ -204,13 +217,12 @@ static OFCondition convertCharacterSet(const char *ifname, OFCondition result = EC_IllegalParameter; if (ifname != NULL) { - DcmDataset *dset = dfile.getDataset(); /* convert all DICOM strings to UTF-8 (if requested) */ if (convertToUTF8) { OFLOG_INFO(dcm2xmlLogger, "converting all element values that are affected by SpecificCharacterSet (0008,0005) to UTF-8"); /* expect that SpecificCharacterSet contains the correct value (defined term) */ - result = dset->convertToUTF8(); + result = dfile.convertToUTF8(); if (result.good()) { /* if conversion was successful, set XML character encoding accordingly */ @@ -224,7 +236,7 @@ static OFCondition convertCharacterSet(const char *ifname, (sopClass == UID_MediaStorageDirectoryStorage)) { /* ... with one or more SpecificCharacterSet elements */ - if (dset->tagExistsWithValue(DCM_SpecificCharacterSet, OFTrue /*searchIntoSub*/)) + if (dfile.getDataset()->tagExistsWithValue(DCM_SpecificCharacterSet, OFTrue /*searchIntoSub*/)) { OFLOG_WARN(dcm2xmlLogger, OFFIS_CONSOLE_APPLICATION << ": this is a DICOMDIR file, which can contain more than one " << "SpecificCharacterSet (0008,0005) element ... using option --convert-to-utf8 is strongly recommended"); diff --git a/dcmdata/apps/dcmdecap.cc b/dcmdata/apps/dcmdecap.cc new file mode 100644 index 00000000..faae892e --- /dev/null +++ b/dcmdata/apps/dcmdecap.cc @@ -0,0 +1,296 @@ +/* + * + * Copyright (C) 2007-2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmdata + * + * Author: Marco Eichelberg, Tingyan Xu + * + * Purpose: Extract encapsulated file from DICOM encapsulated storage object + * + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ + +#include "dcmtk/dcmdata/cmdlnarg.h" +#include "dcmtk/ofstd/ofconapp.h" +#include "dcmtk/dcmdata/dcuid.h" /* for dcmtk version name */ +#include "dcmtk/dcmdata/dcistrmz.h" /* for dcmZlibExpectRFC1950Encoding */ +#include "dcmtk/dcmdata/dcdocdec.h" /* for class DcmDocumentDecapsulator */ +#include "dcmtk/dcmdata/dcdict.h" /* for data dictionary */ + +#ifdef WITH_ZLIB +#include /* for zlibVersion() */ +#endif + +#define OFFIS_CONSOLE_APPLICATION "dcmdecap" + +static OFLogger dcmdecapLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION); +static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; + +#define SHORTCOL 3 +#define LONGCOL 20 +#define EXITCODE_EXEC_FAILED 91 + + +static void addInputOptions(OFCommandLine& cmd) +{ + cmd.addGroup("input options:"); + cmd.addSubGroup("input file format:"); + cmd.addOption("--read-file", "+f", "read file format or data set (default)"); + cmd.addOption("--read-file-only", "+fo", "read file format only"); + cmd.addOption("--read-dataset", "-f", "read data set without file meta information"); + + cmd.addSubGroup("input transfer syntax:"); + cmd.addOption("--read-xfer-auto", "-t=", "use TS recognition (default)"); + cmd.addOption("--read-xfer-detect", "-td", "ignore TS specified in the file meta header"); + cmd.addOption("--read-xfer-little", "-te", "read with explicit VR little endian TS"); + cmd.addOption("--read-xfer-big", "-tb", "read with explicit VR big endian TS"); + cmd.addOption("--read-xfer-implicit", "-ti", "read with implicit VR little endian TS"); + + cmd.addSubGroup("parsing of odd-length attributes:"); + cmd.addOption("--accept-odd-length", "+ao", "accept odd length attributes (default)"); + cmd.addOption("--assume-even-length", "+ae", "assume real length is one byte larger"); + + cmd.addSubGroup("handling of undefined length UN elements:"); + cmd.addOption("--enable-cp246", "+ui", "read undefined len UN as implicit VR (default)"); + cmd.addOption("--disable-cp246", "-ui", "read undefined len UN as explicit VR"); + + cmd.addSubGroup("handling of defined length UN elements:"); + cmd.addOption("--retain-un", "-uc", "retain elements as UN (default)"); + cmd.addOption("--convert-un", "+uc", "convert to real VR if known"); + + cmd.addSubGroup("automatic data correction:"); + cmd.addOption("--enable-correction", "+dc", "enable automatic data correction (default)"); + cmd.addOption("--disable-correction", "-dc", "disable automatic data correction"); + +#ifdef WITH_ZLIB + cmd.addSubGroup("bitstream format of deflated input:"); + cmd.addOption("--bitstream-deflated", "+bd", "expect deflated bitstream (default)"); + cmd.addOption("--bitstream-zlib", "+bz", "expect deflated zlib bitstream"); +#endif +} + +static void parseInputOptions(OFConsoleApplication& app, OFCommandLine& cmd, DcmDocumentDecapsulator& docdec) +{ + E_FileReadMode opt_readMode = ERM_autoDetect; + E_TransferSyntax opt_ixfer = EXS_Unknown; + + cmd.beginOptionBlock(); + if (cmd.findOption("--read-file")) + { + opt_readMode = ERM_autoDetect; + docdec.setReadMode(opt_readMode); + } + if (cmd.findOption("--read-file-only")) + { + opt_readMode = ERM_fileOnly; + docdec.setReadMode(opt_readMode); + } + if (cmd.findOption("--read-dataset")) + { + opt_readMode = ERM_dataset; + docdec.setReadMode(opt_readMode); + } + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--read-xfer-auto")) + { + opt_ixfer = EXS_Unknown; + docdec.setInputXferSyntax(opt_ixfer); + } + if (cmd.findOption("--read-xfer-detect")) + { + dcmAutoDetectDatasetXfer.set(OFTrue); + } + if (cmd.findOption("--read-xfer-little")) + { + opt_ixfer = EXS_LittleEndianExplicit; + app.checkDependence("--read-xfer-little", "--read-dataset", opt_readMode == ERM_dataset); + docdec.setInputXferSyntax(opt_ixfer); + } + if (cmd.findOption("--read-xfer-big")) + { + opt_ixfer = EXS_BigEndianExplicit; + app.checkDependence("--read-xfer-big", "--read-dataset", opt_readMode == ERM_dataset); + docdec.setInputXferSyntax(opt_ixfer); + } + if (cmd.findOption("--read-xfer-implicit")) + { + opt_ixfer = EXS_LittleEndianImplicit; + app.checkDependence("--read-xfer-implicit", "--read-dataset", opt_readMode == ERM_dataset); + docdec.setInputXferSyntax(opt_ixfer); + } + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--accept-odd-length")) + { + dcmAcceptOddAttributeLength.set(OFTrue); + } + if (cmd.findOption("--assume-even-length")) + { + dcmAcceptOddAttributeLength.set(OFFalse); + } + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--enable-cp246")) + { + dcmEnableCP246Support.set(OFTrue); + } + if (cmd.findOption("--disable-cp246")) + { + dcmEnableCP246Support.set(OFFalse); + } + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--retain-un")) + { + dcmEnableUnknownVRConversion.set(OFFalse); + } + if (cmd.findOption("--convert-un")) + { + dcmEnableUnknownVRConversion.set(OFTrue); + } + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--enable-correction")) + { + dcmEnableAutomaticInputDataCorrection.set(OFTrue); + } + if (cmd.findOption("--disable-correction")) + { + dcmEnableAutomaticInputDataCorrection.set(OFFalse); + } + cmd.endOptionBlock(); + +#ifdef WITH_ZLIB + cmd.beginOptionBlock(); + if (cmd.findOption("--bitstream-deflated")) + { + dcmZlibExpectRFC1950Encoding.set(OFFalse); + } + if (cmd.findOption("--bitstream-zlib")) + { + dcmZlibExpectRFC1950Encoding.set(OFTrue); + } + cmd.endOptionBlock(); +#endif +} + +static void addProcessingOptions(OFCommandLine& cmd) +{ + cmd.addGroup("processing options:"); + cmd.addSubGroup("execution options:"); + cmd.addOption("--exec", "-x", 1, "[c]ommand: string", + "execute command c after document extraction"); +} + +static void parseProcessingOptions(OFConsoleApplication& app, OFCommandLine& cmd, DcmDocumentDecapsulator& docdec) +{ + const char *c; + if (cmd.findOption("--exec")) + { + app.checkValue(cmd.getValue(c)); + docdec.setExecString(c); + } +} + +static void addCommandLineOptions(OFCommandLine& cmd) +{ + cmd.setOptionColumns(LONGCOL, SHORTCOL); + cmd.setParamColumn(LONGCOL + SHORTCOL + 4); + cmd.addParam("dcmfile-in", "DICOM input filename (\"-\" for stdin)"); + cmd.addParam("encfile-out", "Encapsulated document output filename\n(\"-\" for stdout)"); + cmd.addGeneralOptions(LONGCOL, SHORTCOL); + + OFLog::addOptions(cmd); + + addInputOptions(cmd); + addProcessingOptions(cmd); +} + +static void parseCommandLineOptions(OFConsoleApplication& app, OFCommandLine& cmd, DcmDocumentDecapsulator& docdec) +{ + const char *c; + cmd.getParam(1, c); + docdec.setInputFile(c); + cmd.getParam(2, c); + docdec.setOutputFile(c); + OFLog::configureFromCommandLine(cmd, app); + parseInputOptions(app, cmd, docdec); + parseProcessingOptions(app, cmd, docdec); +} + + +int main(int argc, char* argv[]) +{ + DcmDocumentDecapsulator docdec; + OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Extract file from DICOM encapsulated storage", rcsid); + OFCommandLine cmd; + addCommandLineOptions(cmd); + + // evaluate command line + prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); + if (app.parseCommandLine(cmd, argc, argv)) + { + // check exclusive options first + if (cmd.hasExclusiveOption()) + { + if (cmd.findOption("--version")) + { + app.printHeader(OFTrue /*print host identifier*/); + COUT << OFendl << "External libraries used:"; +#ifdef WITH_ZLIB + COUT << OFendl << "- ZLIB, Version " << zlibVersion() << OFendl; +#else + COUT << " none" << OFendl; +#endif + return EXITCODE_NO_ERROR; + } + } + + // parse command line parameters and options + parseCommandLineOptions(app, cmd, docdec); + } + + // print resource identifier + OFLOG_DEBUG(dcmdecapLogger, rcsid << OFendl); + + // make sure data dictionary is loaded + if (!dcmDataDict.isDictionaryLoaded()) + { + OFLOG_WARN(dcmdecapLogger, "no data dictionary loaded, check environment variable: " + << DCM_DICT_ENVIRONMENT_VARIABLE); + } + + // read the DICOM file + OFCondition cond = docdec.loadDICOMFile(); + if (cond == EC_InvalidFilename) return EXITCODE_NO_INPUT_FILES; + else if (cond.bad()) return EXITCODE_CANNOT_READ_INPUT_FILE; + + // extract the encapsulated document and write to file + cond = docdec.writeEncapsulatedContentToFile(); + if (cond == EC_MissingAttribute) return EXITCODE_INVALID_INPUT_FILE; + else if (cond.bad()) return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; + + // execute the command line (if defined) + cond = docdec.executeCommand(); + if (cond.bad()) return EXITCODE_EXEC_FAILED; + + // all done + return EXITCODE_NO_ERROR; +} diff --git a/dcmdata/apps/dcmencap.cc b/dcmdata/apps/dcmencap.cc new file mode 100644 index 00000000..abc97683 --- /dev/null +++ b/dcmdata/apps/dcmencap.cc @@ -0,0 +1,113 @@ +/* + * + * Copyright (C) 2018-2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmdata + * + * Author: Pedro Arizpe, Marco Eichelberg + * + * Purpose: Encapsulate document into DICOM format + * + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first*/ +#include "dcmtk/dcmdata/dcencdoc.h" +#include "dcmtk/dcmdata/cmdlnarg.h" +#include "dcmtk/dcmdata/dcuid.h" +#include "dcmtk/dcmdata/dcdict.h" +#include "dcmtk/ofstd/ofconapp.h" + + +#ifdef WITH_ZLIB +#include /* for zlibVersion() */ +#endif + +#define OFFIS_CONSOLE_APPLICATION "dcmencap" + +static OFLogger dcmencapLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION); + +static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" +OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; + +int main(int argc, char *argv[]) +{ + OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Encapsulate document into DICOM format", rcsid); + OFCommandLine cmd; + OFCondition result = EC_Normal; + dcmEnableGenerationOfNewVRs(); + + // process command line options + DcmEncapsulatedDocument encapsulator; + encapsulator.addCommandlineOptions(cmd); + prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); + + if (app.parseCommandLine(cmd, argc, argv)) + { + OFLOG_TRACE (dcmencapLogger, "Checking exclusive options first"); + if (cmd.hasExclusiveOption()) + { + if (cmd.findOption("--version")) + { + app.printHeader(OFTrue /*print host identifier*/); + COUT << OFendl << "External libraries used: "; +#ifdef WITH_ZLIB + COUT << OFendl << "- ZLIB, Version " << zlibVersion() << OFendl; +#else + COUT << " none" << OFendl; +#endif + return EXITCODE_NO_ERROR; + } + } + encapsulator.parseArguments(app, cmd); + } + + //print resource identifier + OFLOG_DEBUG(dcmencapLogger, rcsid << OFendl); + + OFLOG_DEBUG(dcmencapLogger, "making sure data dictionary is loaded"); + if (!dcmDataDict.isDictionaryLoaded()) + { + OFLOG_WARN(dcmencapLogger, "no data dictionary loaded, check environment variable: " + << DCM_DICT_ENVIRONMENT_VARIABLE); + } + + // read input document and place it into the dataset. + // This call will also determine the file type (unless set by command line option). + OFLOG_INFO(dcmencapLogger, "reading file '" << encapsulator.getInputFileName() << "'"); + result = encapsulator.insertEncapsulatedDocument(); + if (result.bad()) return EXITCODE_INVALID_INPUT_FILE; + + // read series file (if any) and otherwise generate study and series UID + result = encapsulator.createIdentifiers(); + if (result.bad()) return EXITCODE_INVALID_INPUT_FILE; + + // perform document format specific processing (such as reading information from the CDA content) + result = encapsulator.formatSpecificProcessing(); + if (result.bad()) return EXITCODE_INVALID_INPUT_FILE; + + // write DICOM header attributes to dataset + result = encapsulator.createHeader(); + if (result.bad()) return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; + + // apply command line override keys (if any) + result = encapsulator.applyOverrideKeys(); + if (result.bad()) return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; + + OFLOG_INFO(dcmencapLogger, "writing encapsulated DICOM object as file '" << encapsulator.getOutputFileName() << "'"); + + // write DICOM file + result = encapsulator.saveFile(); + if (result.bad()) return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; + + OFLOG_INFO(dcmencapLogger, "DICOM encapsulation successful"); + return EXITCODE_NO_ERROR; +} diff --git a/dcmdata/apps/dump2dcm.cc b/dcmdata/apps/dump2dcm.cc index 62326e74..6acb9893 100644 --- a/dcmdata/apps/dump2dcm.cc +++ b/dcmdata/apps/dump2dcm.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -79,10 +79,7 @@ // if defined, use createValueFromTempFile() for large binary data files //#define EXPERIMENTAL_READ_FROM_FILE -#ifdef HAVE_SYS_TYPES_H #include -#endif - #include "dcmtk/ofstd/ofstd.h" #include "dcmtk/ofstd/ofconapp.h" #include "dcmtk/ofstd/ofstream.h" diff --git a/dcmdata/apps/img2dcm.cc b/dcmdata/apps/img2dcm.cc index 916b388d..02252997 100644 --- a/dcmdata/apps/img2dcm.cc +++ b/dcmdata/apps/img2dcm.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2007-2023, OFFIS e.V. + * Copyright (C) 2007-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -469,6 +469,9 @@ static OFCondition startConversion( cond = i2d.convertNextFrame(inputPlug, ++frameNum); } + // adjust byte order of pixel data to local OW byte order + if (cond.good()) cond = i2d.adjustByteOrder(inputFiles.size()); + // update offset table if image type is encapsulated if (cond.good()) cond = i2d.updateOffsetTable(); diff --git a/dcmdata/apps/json2dcm.cc b/dcmdata/apps/json2dcm.cc new file mode 100644 index 00000000..e26b0e8d --- /dev/null +++ b/dcmdata/apps/json2dcm.cc @@ -0,0 +1,493 @@ +/* + * + * Copyright (C) 2024-2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmdata + * + * Author: Tingyan Xu + * + * Purpose: Convert JSON DICOM document to binary DICOM file + * + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/ofstd/ofconapp.h" +#include "dcmtk/dcmdata/cmdlnarg.h" +#include "dcmtk/dcmdata/dcostrmz.h" /* for dcmZlibCompressionLevel */ +#include "dcmtk/dcmdata/dcfilefo.h" /* for class DcmFileFormat */ +#include "dcmtk/dcmdata/dcdeftag.h" /* for tag keys */ +#include "dcmtk/dcmdata/dcuid.h" /* for UID constants */ +#include "dcmtk/dcmdata/dcdict.h" /* for the data dictionary */ +#include "dcmtk/dcmdata/dcjsonrd.h" /* for class DcmJSONReader */ + +#ifdef WITH_ZLIB +#include /* for zlibVersion() */ +#endif + +#define OFFIS_CONSOLE_APPLICATION "json2dcm" +#define OFFIS_CONSOLE_DESCRIPTION "Convert JSON document to DICOM file or data set" + +/* Error codes */ + +#define EXITCODE_INVALID_JSON_CONTENT 65 +#define EXITCODE_BULKDATA_URI_NOT_SUPPORTED 66 + +static OFLogger json2dcmLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION); + +static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" +OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; + +void addInputOptions(OFCommandLine& cmd) +{ + cmd.addGroup("input options:"); + cmd.addSubGroup("input file format:"); + cmd.addOption("--read-meta-info", "+f", "read meta information if present (default)"); + cmd.addOption("--ignore-meta-info", "-f", "ignore file meta information"); +} + +void addProcessOptions(OFCommandLine& cmd) +{ + cmd.addGroup("processing options:"); + cmd.addSubGroup("unique identifiers:"); + cmd.addOption("--generate-new-uids", "+Ug", "generate new Study/Series/SOP Instance UID"); + cmd.addOption("--dont-overwrite-uids", "-Uo", "do not overwrite existing UIDs (default)"); + cmd.addOption("--overwrite-uids", "+Uo", "overwrite existing UIDs"); + cmd.addSubGroup("bulkdata URI handling:"); + cmd.addOption("--parse-bulkdata-uri", "+Bu", "parse Bulkdata URIs (default)"); + cmd.addOption("--ignore-bulkdata-uri", "-Bu", "ignore Bulkdata URIs"); + cmd.addOption("--add-bulkdata-dir", "+Bd", 1, "[d]irectory: string", + "add d to list of permitted bulk data sources"); + cmd.addSubGroup("handling of arrays with multiple data sets:"); + cmd.addOption("--array-reject", "-ar", "reject multiple data sets (default)"); + cmd.addOption("--array-select", "+as", 1, "[n]umber: integer", + "select data set n from array"); + cmd.addOption("--array-sequence", "+ar", "store all data sets in private sequence"); +} + +void addOutputOptions(OFCommandLine& cmd) +{ + cmd.addGroup("output options:"); + cmd.addSubGroup("output file format:"); + cmd.addOption("--write-file", "+F", "write file format (default)"); + cmd.addOption("--write-dataset", "-F", "write data set without file meta information"); + cmd.addOption("--update-meta-info", "+Fu", "update particular file meta information"); + cmd.addSubGroup("output transfer syntax:"); + cmd.addOption("--write-xfer-same", "+t=", "write with same TS as input (default)"); + cmd.addOption("--write-xfer-little", "+te", "write with explicit VR little endian TS"); + cmd.addOption("--write-xfer-big", "+tb", "write with explicit VR big endian TS"); + cmd.addOption("--write-xfer-implicit", "+ti", "write with implicit VR little endian TS"); +#ifdef WITH_ZLIB + cmd.addOption("--write-xfer-deflated", "+td", "write with deflated expl. VR little endian TS"); +#endif + cmd.addSubGroup("error handling:"); + cmd.addOption("--stop-on-error", "-E", "do not write if document is invalid (default)"); + cmd.addOption("--ignore-errors", "+E", "attempt to write even if document is invalid"); + cmd.addSubGroup("post-1993 value representations:"); + cmd.addOption("--enable-new-vr", "+u", "enable support for new VRs (UN/UT) (default)"); + cmd.addOption("--disable-new-vr", "-u", "disable support for new VRs, convert to OB"); + cmd.addSubGroup("length encoding in sequences and items:"); + cmd.addOption("--length-explicit", "+e", "write with explicit lengths (default)"); + cmd.addOption("--length-undefined", "-e", "write with undefined lengths"); + cmd.addSubGroup("charset handling:"); + cmd.addOption("--charset-accept", "+c", "write with the given charset in JSON (default)"); + cmd.addOption("--charset-replace", "-c", "replace the given charset in JSON with UTF-8"); +#ifdef WITH_ZLIB + cmd.addSubGroup("deflate compression level (only with --write-xfer-deflated):"); + cmd.addOption("--compression-level", "+cl", 1, "[l]evel: integer (default: 6)", + "0=uncompressed, 1=fastest, 9=best compression"); +#endif +} + +#define SHORTCOL 3 +#define LONGCOL 21 + +void addJSON2DCMCommandlineOptions(OFCommandLine& cmd) +{ + cmd.setOptionColumns(LONGCOL, SHORTCOL); + cmd.setParamColumn(LONGCOL + SHORTCOL + 4); + + cmd.addParam("jsonfile-in", "JSON input filename to be converted\n(\"-\" for stdin)"); + cmd.addParam("dcmfile-out", "DICOM output filename\n(\"-\" for stdout)"); + + cmd.addGeneralOptions(LONGCOL, SHORTCOL); + OFLog::addOptions(cmd); + addInputOptions(cmd); + addProcessOptions(cmd); + addOutputOptions(cmd); +} + +/** + * function for parsing commandline arguments + */ +OFCondition parseArguments( + OFConsoleApplication& app, + OFCommandLine& cmd, + OFBool& opt_generateUIDs, + OFBool& opt_overwriteUIDs, + E_TransferSyntax& opt_xfer, + E_EncodingType& opt_enctype, + OFBool& opt_replaceCharset, + E_FileWriteMode& opt_writeMode, + const char*& opt_ifname, + const char*& opt_ofname, + DcmJSONReader& jsonReader +) +{ + cmd.getParam(1, opt_ifname); + cmd.getParam(2, opt_ofname); + + OFLog::configureFromCommandLine(cmd, app); + + /* input options */ + cmd.beginOptionBlock(); + if (cmd.findOption("--read-meta-info")) + jsonReader.setIgnoreMetaInfoPolicy(OFFalse); + if (cmd.findOption("--ignore-meta-info")) + jsonReader.setIgnoreMetaInfoPolicy(OFTrue); + cmd.endOptionBlock(); + + /* processing options */ + if (cmd.findOption("--generate-new-uids")) + opt_generateUIDs = OFTrue; + + cmd.beginOptionBlock(); + if (cmd.findOption("--dont-overwrite-uids")) + { + app.checkDependence("--dont-overwrite-uids", "--generate-new-uids", opt_generateUIDs); + opt_overwriteUIDs = OFFalse; + } + if (cmd.findOption("--overwrite-uids")) + { + app.checkDependence("--overwrite-uids", "--generate-new-uids", opt_generateUIDs); + opt_overwriteUIDs = OFTrue; + } + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--ignore-bulkdata-uri")) + { + jsonReader.setIgnoreBulkdataURIPolicy(OFTrue); + } + if (cmd.findOption("--parse-bulkdata-uri")) + { + jsonReader.setIgnoreBulkdataURIPolicy(OFFalse); + } + cmd.endOptionBlock(); + + if (cmd.findOption("--add-bulkdata-dir", 0, OFCommandLine::FOM_First)) + { + OFCondition result; + const char *current = NULL; + do + { + app.checkValue(cmd.getValue(current)); + result = jsonReader.addPermittedBulkdataPath(current); + if (result.bad()) return result; + } while (cmd.findOption("--add-bulkdata-dir", 0, OFCommandLine::FOM_Next)); + } + + cmd.beginOptionBlock(); + if (cmd.findOption("--array-reject")) + { + jsonReader.setArrayHandlingPolicy(-1); + } + if (cmd.findOption("--array-sequence")) + { + jsonReader.setArrayHandlingPolicy(0); + } + if (cmd.findOption("--array-select")) + { + OFCmdSignedInt arrayHandling = -1; + cmd.getValueAndCheckMin(arrayHandling, 1); + jsonReader.setArrayHandlingPolicy(arrayHandling); + } + cmd.endOptionBlock(); + + /* output options */ + cmd.beginOptionBlock(); + if (cmd.findOption("--write-file")) + opt_writeMode = EWM_fileformat; + if (cmd.findOption("--write-dataset")) + opt_writeMode = EWM_dataset; + cmd.endOptionBlock(); + + if (cmd.findOption("--update-meta-info")) + { + app.checkConflict("--update-meta-info", "--write-dataset", opt_writeMode == EWM_dataset); + opt_writeMode = EWM_updateMeta; + } + + cmd.beginOptionBlock(); + if (cmd.findOption("--write-xfer-same")) + opt_xfer = EXS_Unknown; + if (cmd.findOption("--write-xfer-little")) + opt_xfer = EXS_LittleEndianExplicit; + if (cmd.findOption("--write-xfer-big")) + opt_xfer = EXS_BigEndianExplicit; + if (cmd.findOption("--write-xfer-implicit")) + opt_xfer = EXS_LittleEndianImplicit; +#ifdef WITH_ZLIB + if (cmd.findOption("--write-xfer-deflated")) + opt_xfer = EXS_DeflatedLittleEndianExplicit; +#endif + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--stop-on-error")) + jsonReader.setStopOnErrorPolicy(OFTrue); + if (cmd.findOption("--ignore-errors")) + jsonReader.setStopOnErrorPolicy(OFFalse); + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--enable-new-vr")) + dcmEnableGenerationOfNewVRs(); + if (cmd.findOption("--disable-new-vr")) + dcmDisableGenerationOfNewVRs(); + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--length-explicit")) + opt_enctype = EET_ExplicitLength; + if (cmd.findOption("--length-undefined")) + opt_enctype = EET_UndefinedLength; + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--charset-accept")) + opt_replaceCharset = OFFalse; + if (cmd.findOption("--charset-replace")) + opt_replaceCharset = OFTrue; + cmd.endOptionBlock(); + +#ifdef WITH_ZLIB + if (cmd.findOption("--compression-level")) + { + OFCmdUnsignedInt comprLevel = 0; + app.checkDependence("--compression-level", "--write-xfer-deflated", opt_xfer == EXS_DeflatedLittleEndianExplicit); + app.checkValue(cmd.getValueAndCheckMinMax(comprLevel, 0, 9)); + dcmZlibCompressionLevel.set(OFstatic_cast(int, comprLevel)); + } +#endif + + return EC_Normal; +} + +void generateUIDs(DcmItem *dataset, OFBool overwriteUIDs, E_FileWriteMode& writeMode) +{ + char uid[100]; + if (overwriteUIDs || !dataset->tagExistsWithValue(DCM_StudyInstanceUID)) + { + OFLOG_INFO(json2dcmLogger, "generating new Study Instance UID"); + dataset->putAndInsertString(DCM_StudyInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_STUDY_UID_ROOT)); + } + if (overwriteUIDs || !dataset->tagExistsWithValue(DCM_SeriesInstanceUID)) + { + OFLOG_INFO(json2dcmLogger, "generating new Series Instance UID"); + dataset->putAndInsertString(DCM_SeriesInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_SERIES_UID_ROOT)); + } + if (overwriteUIDs || !dataset->tagExistsWithValue(DCM_SOPInstanceUID)) + { + OFLOG_INFO(json2dcmLogger, "generating new SOP Instance UID"); + dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT)); + + /* make sure that the file meta information is updated correspondingly */ + if (writeMode == EWM_fileformat) + writeMode = EWM_updateMeta; + } +} + +void updateCharacterSet(DcmItem *dataset, const char *ifname, OFBool replaceCharset) +{ + OFString allowedCharset = "ISO_IR 192"; + OFString asciiCharset = "ISO_IR 6"; + const char *elemValue; + OFCondition result = dataset->findAndGetString(DCM_SpecificCharacterSet, elemValue, OFTrue /*searchIntoSub*/); + if (replaceCharset) + { + if (result.bad() || elemValue == NULL) + { + OFLOG_WARN(json2dcmLogger, "JSON file '" << ifname << "' does not specify a character set, it will be set to UTF-8 ('" << allowedCharset << "')"); + dataset->putAndInsertString(DCM_SpecificCharacterSet, allowedCharset.c_str()); + } + else if (allowedCharset.compare(elemValue) != 0) + { + OFLOG_WARN(json2dcmLogger, "JSON file '" << ifname << "' specifies a character set other than UTF-8, it will be set to UTF-8 ('" << allowedCharset << "')"); + dataset->putAndInsertString(DCM_SpecificCharacterSet, allowedCharset.c_str()); + } + } + else + { + if ((result.bad() || elemValue == NULL)) + { + if (dataset->containsExtendedCharacters()) + { + // JSON dataset contains non-ASCII characters but no specific character set + OFLOG_WARN(json2dcmLogger, "JSON file '" << ifname << "' does not specify a character set, the file is encoded in UTF-8 ('" << allowedCharset << "')"); + } + } + else if (allowedCharset.compare(elemValue) != 0) + { + // JSON dataset specifies a specific character set other then ISO_IR 192 + if (dataset->containsExtendedCharacters() || (asciiCharset.compare(elemValue) != 0)) + { + // JSON dataset is not ASCII (ISO_IR 6) either + OFLOG_WARN(json2dcmLogger, "JSON file '" << ifname << "' specifies the character set '" + << elemValue << "' but the file is encoded in UTF-8 ('" << allowedCharset << "')"); + } + } + } +} + +int main(int argc, char *argv[]) +{ + OFBool opt_generateUIDs = OFFalse; + OFBool opt_overwriteUIDs = OFFalse; + E_TransferSyntax opt_xfer = EXS_Unknown; + E_EncodingType opt_enctype = EET_ExplicitLength; + OFBool opt_replaceCharset = OFFalse; + E_FileWriteMode opt_writeMode = EWM_fileformat; + + const char *opt_ifname = NULL; + const char *opt_ofname = NULL; + + /* set-up command line parameters and options */ + OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, OFFIS_CONSOLE_DESCRIPTION, rcsid); + OFCommandLine cmd; + + // add supported command line options + addJSON2DCMCommandlineOptions(cmd); + + // evaluate command line + prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); + if (app.parseCommandLine(cmd, argc, argv)) + { + // checking exclusive options first + if (cmd.hasExclusiveOption()) + { + if (cmd.findOption("--version")) + { + app.printHeader(OFTrue /*print host identifier*/); + COUT << OFendl << "External libraries used:"; +#ifdef WITH_ZLIB + COUT << OFendl << "- ZLIB, Version " << zlibVersion() << OFendl; +#else + COUT << " none" << OFendl; +#endif + return EXITCODE_NO_ERROR; + } + } + } + + /* print resource identifier */ + OFLOG_DEBUG(json2dcmLogger, rcsid << OFendl); + + /* make sure data dictionary is loaded */ + if (!dcmDataDict.isDictionaryLoaded()) + { + OFLOG_WARN(json2dcmLogger, "no data dictionary loaded, check environment variable: " + << DCM_DICT_ENVIRONMENT_VARIABLE); + } + + DcmFileFormat fileformat; + DcmJSONReader jsonReader; + + /* command line parameters and options */ + OFCondition result = parseArguments( + app, cmd, + opt_generateUIDs, + opt_overwriteUIDs, + opt_xfer, + opt_enctype, + opt_replaceCharset, + opt_writeMode, + opt_ifname, + opt_ofname, + jsonReader + ); + + if (result.bad()) + return EXITCODE_COMMANDLINE_SYNTAX_ERROR; + + // check filenames + if ((opt_ifname == NULL) || (strlen(opt_ifname) == 0)) + { + OFLOG_ERROR(json2dcmLogger, OFFIS_CONSOLE_APPLICATION << ": invalid input filename: "); + return EXITCODE_COMMANDLINE_SYNTAX_ERROR; + } + if ((opt_ofname == NULL) || (strlen(opt_ofname) == 0)) + { + OFLOG_ERROR(json2dcmLogger, OFFIS_CONSOLE_APPLICATION << ": invalid output filename: "); + return EXITCODE_COMMANDLINE_SYNTAX_ERROR; + } + + // read JSON file and convert to DICOM + OFLOG_INFO(json2dcmLogger, "reading JSON input file: " << opt_ifname); + result = jsonReader.readAndConvertJSONFile(fileformat, opt_ifname); + if (result.bad()) + { + if (result == EC_BulkDataURINotSupported) + return EXITCODE_BULKDATA_URI_NOT_SUPPORTED; + else if (result == EC_InvalidFilename) + return EXITCODE_CANNOT_READ_INPUT_FILE; + else + return EXITCODE_INVALID_JSON_CONTENT; + } + + // generate new UIDs (if required) + if (opt_generateUIDs) + { + generateUIDs(fileformat.getDataset(), opt_overwriteUIDs, opt_writeMode); + } + + // check and update SpecificCharacterSet + updateCharacterSet(fileformat.getDataset(), opt_ifname, opt_replaceCharset); + + // determine the transfer syntax for writing the file + if (opt_xfer == EXS_Unknown) + { + /* use Explicit VR Little Endian as default if no transfer syntax is given otherwise */ + if (jsonReader.getTransferSyntax() == EXS_Unknown) + opt_xfer = EXS_LittleEndianExplicit; + else + opt_xfer = jsonReader.getTransferSyntax(); + } + + // check whether it is possible to write the file in the requested transfer syntax + if (fileformat.canWriteXfer(opt_xfer)) + { + // encapsulated pixel data requires a DICOM file, in this case ignore --write-dataset + if ((opt_writeMode == EWM_dataset) && DcmXfer(opt_xfer).usesEncapsulatedFormat()) + { + OFLOG_WARN(json2dcmLogger, "encapsulated pixel data require file format, ignoring --write-dataset"); + opt_writeMode = EWM_fileformat; + } + + OFLOG_INFO(json2dcmLogger, "writing DICOM output file: " << opt_ofname); + result = fileformat.saveFile(opt_ofname, opt_xfer, opt_enctype, EGL_withoutGL, EPD_noChange, + OFstatic_cast(Uint32, 0), OFstatic_cast(Uint32, 0), opt_writeMode); + + if (result.bad()) + { + OFLOG_ERROR(json2dcmLogger, result.text() << ": writing file: " << opt_ofname); + return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; + } + } + else + { + OFLOG_ERROR(json2dcmLogger, "no conversion to transfer syntax " << DcmXfer(opt_xfer).getXferName() << " possible!"); + return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; + } + + return 0; +} + diff --git a/dcmdata/apps/mdfdsman.cc b/dcmdata/apps/mdfdsman.cc index 26d288f6..0cdccd83 100644 --- a/dcmdata/apps/mdfdsman.cc +++ b/dcmdata/apps/mdfdsman.cc @@ -618,7 +618,9 @@ OFCondition MdfDatasetManager::saveFile(const char* file_name, { opt_xfer = EXS_LittleEndianExplicit; } - /* write DICOM file */ + /* write DICOM file. We deliberately use write mode EWM_fileformat here because + * EWM_createNewMeta would break the "--no-meta-uid" command line option + */ result = dfile->saveFile(file_name, opt_xfer, opt_enctype, @@ -626,7 +628,7 @@ OFCondition MdfDatasetManager::saveFile(const char* file_name, opt_padenc, OFstatic_cast(Uint32, opt_filepad), OFstatic_cast(Uint32, opt_itempad), - (opt_dataset) ? EWM_dataset : EWM_createNewMeta); + (opt_dataset) ? EWM_dataset : EWM_fileformat); } else { diff --git a/dcmdata/apps/pdf2dcm.cc b/dcmdata/apps/pdf2dcm.cc index 95839753..4fefe48f 100644 --- a/dcmdata/apps/pdf2dcm.cc +++ b/dcmdata/apps/pdf2dcm.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2005-2019, OFFIS e.V. + * Copyright (C) 2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -13,122 +13,33 @@ * * Module: dcmdata * - * Author: Marco Eichelberg, Pedro Arizpe + * Authors: Marco Eichelberg * - * Purpose: Encapsulate PDF file into a DICOM file + * Purpose: Proxy stub that calls dcmencap * */ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ -#include "dcmtk/dcmdata/dctk.h" -#include "dcmtk/dcmdata/dcencdoc.h" -#include "dcmtk/ofstd/ofconapp.h" +#include "dcmtk/ofstd/ofstub.h" +#include "dcmtk/ofstd/ofstd.h" +#include -#ifdef WITH_ZLIB -#include /* for zlibVersion() */ -#endif - -#define OFFIS_CONSOLE_APPLICATION "pdf2dcm" - -static OFLogger pdf2dcmLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION); - -static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" -OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; - -int main(int argc, char *argv[]) +int main(int argc, char** argv) { - OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Encapsulate PDF file into DICOM format", rcsid); - OFCommandLine cmd; - int errorCode = EXITCODE_NO_ERROR; - OFCondition result = EC_Normal; - DcmFileFormat fileformat; - DcmEncapsulatedDocument encapsulator; - - OFLOG_TRACE(pdf2dcmLogger, "Including necessary options"); - encapsulator.addPDFCommandlineOptions(cmd); - OFLOG_TRACE(pdf2dcmLogger, "Evaluating command line"); - prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); - - if (app.parseCommandLine(cmd, argc, argv)) { - OFLOG_TRACE(pdf2dcmLogger, "Checking exclusive options first"); - if (cmd.hasExclusiveOption()) - { - if (cmd.findOption("--version")) - { - app.printHeader(OFTrue /*print host identifier*/); - COUT << OFendl << "External libraries used: "; -#ifdef WITH_ZLIB - COUT << OFendl << "- ZLIB, Version " << zlibVersion() << OFendl; -#else - COUT << " none" << OFendl; -#endif - return EXITCODE_NO_ERROR; - } - } - encapsulator.parseArguments(app, cmd); - } - //print resource identifier - OFLOG_DEBUG(pdf2dcmLogger, rcsid << OFendl); - - OFLOG_DEBUG(pdf2dcmLogger, "making sure data dictionary is loaded"); - if (!dcmDataDict.isDictionaryLoaded()) - { - OFLOG_WARN(pdf2dcmLogger, "no data dictionary loaded, check environment variable: " - << DCM_DICT_ENVIRONMENT_VARIABLE); - } - OFLOG_DEBUG(pdf2dcmLogger, "Creating identifiers (and reading series data)"); - result = encapsulator.createIdentifiers(pdf2dcmLogger); - if (result.bad()) - { - OFLOG_FATAL(pdf2dcmLogger, "There was an error while reading the series data"); - return EXITCODE_INVALID_INPUT_FILE; - } - OFLOG_INFO(pdf2dcmLogger, "creating encapsulated PDF object"); - errorCode = encapsulator.insertEncapsulatedDocument(fileformat.getDataset(), pdf2dcmLogger); - if (errorCode != EXITCODE_NO_ERROR) - { - OFLOG_ERROR(pdf2dcmLogger, "unable to create PDF encapsulation to DICOM format"); - return errorCode; - } - OFLOG_INFO(pdf2dcmLogger, "Generating an instance number that is guaranteed to be unique within a series."); - result = encapsulator.createHeader(fileformat.getDataset(), pdf2dcmLogger); - if (result.bad()) - { - OFLOG_ERROR(pdf2dcmLogger, "unable to create DICOM header: " << result.text()); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - OFLOG_INFO(pdf2dcmLogger, "writing encapsulated PDF object as file " << encapsulator.getOutputFileName()); - - OFLOG_INFO(pdf2dcmLogger, "Check if new output transfer syntax is possible"); - - DcmXfer opt_oxferSyn(encapsulator.getTransferSyntax()); - - fileformat.getDataset()->chooseRepresentation(encapsulator.getTransferSyntax(), NULL); - if (fileformat.getDataset()->canWriteXfer(encapsulator.getTransferSyntax())) - { - OFLOG_INFO(pdf2dcmLogger, "Output transfer syntax " << opt_oxferSyn.getXferName() << " can be written"); - } - else { - OFLOG_ERROR(pdf2dcmLogger, "No conversion to transfer syntax " << opt_oxferSyn.getXferName() << " possible!"); - return EXITCODE_COMMANDLINE_SYNTAX_ERROR; - } - OFLOG_INFO(pdf2dcmLogger, "Checking for DICOM key overriding"); - result = encapsulator.applyOverrideKeys(fileformat.getDataset()); - if (result.bad()) - { - OFLOG_ERROR(pdf2dcmLogger, "There was a problem while overriding a key:" << OFendl - << result.text()); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - OFLOG_INFO(pdf2dcmLogger, "write converted DICOM file with metaheader"); - result = encapsulator.saveFile(fileformat); - if (result.bad()) - { - OFLOG_ERROR(pdf2dcmLogger, result.text() << ": writing file: " << encapsulator.getOutputFileName()); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - - OFLOG_INFO(pdf2dcmLogger, "PDF encapsulation successful"); - - return EXITCODE_NO_ERROR; + // create an argv array that is one entry larger than the one specified by the user + char **my_argv = new char *[argc+2]; + char filetype[100]; + OFStandard::strlcpy(filetype, "--filetype-pdf", sizeof(filetype)); + + // copy arguments, then add file type argument and NULL pointer + memcpy(my_argv, argv, argc * sizeof(char *)); + my_argv[argc] = filetype; + my_argv[argc+1] = NULL; + + // call stub + int result = OFstub_main(argc+1, my_argv, "pdf2dcm", "dcmencap"); + + // clean up (Windows only, on Posix systems the stub will not return) + delete[] my_argv; + return result; } diff --git a/dcmdata/apps/stl2dcm.cc b/dcmdata/apps/stl2dcm.cc index d684679d..13bf34a9 100644 --- a/dcmdata/apps/stl2dcm.cc +++ b/dcmdata/apps/stl2dcm.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2018-2019, OFFIS e.V. + * Copyright (C) 2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -13,122 +13,33 @@ * * Module: dcmdata * - * Author: Pedro Arizpe + * Authors: Marco Eichelberg * - * Purpose: Encapsulate STL file into a DICOM file + * Purpose: Proxy stub that calls dcmencap * */ -#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first*/ -#include "dcmtk/dcmdata/dctk.h" -#include "dcmtk/dcmdata/dcencdoc.h" -#include "dcmtk/ofstd/ofconapp.h" +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/ofstd/ofstub.h" +#include "dcmtk/ofstd/ofstd.h" +#include -#ifdef WITH_ZLIB -#include /* for zlibVersion() */ -#endif - -#define OFFIS_CONSOLE_APPLICATION "stl2dcm" - -static OFLogger stl2dcmLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION); - -static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" -OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; - -int main(int argc, char *argv[]) +int main(int argc, char** argv) { - OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Encapsulate STL file into DICOM format", rcsid); - OFCommandLine cmd; - int errorCode = EXITCODE_NO_ERROR; - OFCondition result = EC_Normal; - DcmFileFormat fileformat; - DcmEncapsulatedDocument encapsulator; - - OFLOG_TRACE(stl2dcmLogger, "Including necessary options"); - encapsulator.addSTLCommandlineOptions(cmd); - OFLOG_TRACE(stl2dcmLogger, "Evaluating command line"); - prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); - - if (app.parseCommandLine(cmd, argc, argv)) { - OFLOG_TRACE(stl2dcmLogger, "Checking exclusive options first"); - if (cmd.hasExclusiveOption()) - { - if (cmd.findOption("--version")) - { - app.printHeader(OFTrue /*print host identifier*/); - COUT << OFendl << "External libraries used: "; -#ifdef WITH_ZLIB - COUT << OFendl << "- ZLIB, Version " << zlibVersion() << OFendl; -#else - COUT << " none" << OFendl; -#endif - return EXITCODE_NO_ERROR; - } - } - encapsulator.parseArguments(app, cmd); - } - //print resource identifier - OFLOG_DEBUG(stl2dcmLogger, rcsid << OFendl); - - OFLOG_DEBUG(stl2dcmLogger, "making sure data dictionary is loaded"); - if (!dcmDataDict.isDictionaryLoaded()) - { - OFLOG_WARN(stl2dcmLogger, "no data dictionary loaded, check environment variable: " - << DCM_DICT_ENVIRONMENT_VARIABLE); - } - OFLOG_DEBUG(stl2dcmLogger, "Creating identifiers (and reading series data)"); - result = encapsulator.createIdentifiers(stl2dcmLogger); - if (result.bad()) - { - OFLOG_FATAL(stl2dcmLogger, "There was an error while reading the series data"); - return EXITCODE_INVALID_INPUT_FILE; - } - OFLOG_INFO(stl2dcmLogger, "creating encapsulated STL object"); - errorCode = encapsulator.insertEncapsulatedDocument(fileformat.getDataset(), stl2dcmLogger); - if (errorCode != EXITCODE_NO_ERROR) - { - OFLOG_ERROR(stl2dcmLogger, "unable to create STL encapsulation to DICOM format"); - return errorCode; - } - OFLOG_INFO(stl2dcmLogger, "Generating an instance number that is guaranteed to be unique within a series."); - result = encapsulator.createHeader(fileformat.getDataset(), stl2dcmLogger); - if (result.bad()) - { - OFLOG_ERROR(stl2dcmLogger, "unable to create DICOM header: " << result.text()); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - OFLOG_INFO(stl2dcmLogger, "writing encapsulated STL object as file " << encapsulator.getOutputFileName()); - - OFLOG_INFO(stl2dcmLogger, "Check if new output transfer syntax is possible"); - - DcmXfer opt_oxferSyn(encapsulator.getTransferSyntax()); - - fileformat.getDataset()->chooseRepresentation(encapsulator.getTransferSyntax(), NULL); - if (fileformat.getDataset()->canWriteXfer(encapsulator.getTransferSyntax())) - { - OFLOG_INFO(stl2dcmLogger, "Output transfer syntax " << opt_oxferSyn.getXferName() << " can be written"); - } - else { - OFLOG_ERROR(stl2dcmLogger, "No conversion to transfer syntax " << opt_oxferSyn.getXferName() << " possible!"); - return EXITCODE_COMMANDLINE_SYNTAX_ERROR; - } - OFLOG_INFO(stl2dcmLogger, "Checking for DICOM key overriding"); - result = encapsulator.applyOverrideKeys(fileformat.getDataset()); - if (result.bad()) - { - OFLOG_ERROR(stl2dcmLogger, "There was a problem while overriding a key:" << OFendl - << result.text()); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - OFLOG_INFO(stl2dcmLogger, "write converted DICOM file with metaheader"); - result = encapsulator.saveFile(fileformat); - if (result.bad()) - { - OFLOG_ERROR(stl2dcmLogger, result.text() << ": writing file: " << encapsulator.getOutputFileName()); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - - OFLOG_INFO(stl2dcmLogger, "STL encapsulation successful"); - - return EXITCODE_NO_ERROR; + // create an argv array that is one entry larger than the one specified by the user + char **my_argv = new char *[argc+2]; + char filetype[100]; + OFStandard::strlcpy(filetype, "--filetype-stl", sizeof(filetype)); + + // copy arguments, then add file type argument and NULL pointer + memcpy(my_argv, argv, argc * sizeof(char *)); + my_argv[argc] = filetype; + my_argv[argc+1] = NULL; + + // call stub + int result = OFstub_main(argc+1, my_argv, "stl2dcm", "dcmencap"); + + // clean up (Windows only, on Posix systems the stub will not return) + delete[] my_argv; + return result; } diff --git a/dcmdata/data/dicom.dic b/dcmdata/data/dicom.dic index 44f771e8..0798e72a 100644 --- a/dcmdata/data/dicom.dic +++ b/dcmdata/data/dicom.dic @@ -1,5 +1,5 @@ # -# Copyright (C) 1994-2024, OFFIS e.V. +# Copyright (C) 1994-2025, OFFIS e.V. # All rights reserved. See COPYRIGHT file for details. # # This software and supporting documentation were developed by @@ -21,8 +21,8 @@ # DICONDE (Digital Imaging and Communication in Nondestructive Evaluation) and # DICOS (Digital Imaging and Communications in Security) standard. # -# Generated automatically from DICOM PS 3.6-2024e and PS 3.7-2024e. -# File created on 2024-11-16 10:17:21 by J. Riesmeier on thinkpad2. +# Generated automatically from DICOM PS 3.6-2025e and PS 3.7-2025e. +# File created on 2025-11-21 11:51:05 by J. Riesmeier on thinkpad2. # # In addition, the data dictionary entries from the following final text # supplements and correction items (CP) have been incorporated: @@ -300,6 +300,10 @@ (0008,119B) SQ FailedStudySequence 1 DICOM (0008,1200) SQ StudiesContainingOtherReferencedInstancesSequence 1 DICOM (0008,1250) SQ RelatedSeriesSequence 1 DICOM +(0008,1301) SQ PrincipalDiagnosisCodeSequence 1 DICOM +(0008,1302) SQ PrimaryDiagnosisCodeSequence 1 DICOM +(0008,1303) SQ SecondaryDiagnosesCodeSequence 1 DICOM +(0008,1304) SQ HistologicalDiagnosesCodeSequence 1 DICOM (0008,2111) ST DerivationDescription 1 DICOM (0008,2112) SQ SourceImageSequence 1 DICOM (0008,2120) SH StageName 1 DICOM @@ -344,6 +348,12 @@ (0008,9459) FL RecommendedDisplayFrameRateInFloat 1 DICOM (0008,9460) CS SkipFrameRangeFlag 1 DICOM (0010,0010) PN PatientName 1 DICOM +(0010,0011) SQ PersonNamesToUseSequence 1 DICOM +(0010,0012) LT NameToUse 1 DICOM +(0010,0013) UT NameToUseComment 1 DICOM +(0010,0014) SQ ThirdPersonPronounsSequence 1 DICOM +(0010,0015) SQ PronounCodeSequence 1 DICOM +(0010,0016) UT PronounComment 1 DICOM (0010,0020) LO PatientID 1 DICOM (0010,0021) LO IssuerOfPatientID 1 DICOM (0010,0022) CS TypeOfPatientID 1 DICOM @@ -357,6 +367,13 @@ (0010,0034) LO PatientDeathDateInAlternativeCalendar 1 DICOM (0010,0035) CS PatientAlternativeCalendar 1 DICOM (0010,0040) CS PatientSex 1 DICOM +(0010,0041) SQ GenderIdentitySequence 1 DICOM +(0010,0042) UT SexParametersForClinicalUseCategoryComment 1 DICOM +(0010,0043) SQ SexParametersForClinicalUseCategorySequence 1 DICOM +(0010,0044) SQ GenderIdentityCodeSequence 1 DICOM +(0010,0045) UT GenderIdentityComment 1 DICOM +(0010,0046) SQ SexParametersForClinicalUseCategoryCodeSequence 1 DICOM +(0010,0047) UR SexParametersForClinicalUseCategoryReference 1-n DICOM (0010,0050) SQ PatientInsurancePlanCodeSequence 1 DICOM (0010,0101) SQ PatientPrimaryLanguageCodeSequence 1 DICOM (0010,0102) SQ PatientPrimaryLanguageModifierCodeSequence 1 DICOM @@ -395,8 +412,8 @@ (0010,2152) LO RegionOfResidence 1 DICOM (0010,2154) SH PatientTelephoneNumbers 1-n DICOM (0010,2155) LT PatientTelecomInformation 1 DICOM -(0010,2160) SH EthnicGroup 1 DICOM (0010,2161) SQ EthnicGroupCodeSequence 1 DICOM +(0010,2162) UC EthnicGroups 1-n DICOM (0010,2180) SH Occupation 1 DICOM (0010,21A0) CS SmokingStatus 1 DICOM (0010,21B0) LT AdditionalPatientHistory 1 DICOM @@ -614,6 +631,18 @@ (0014,40A0) LO ImageQualityIndicatorType 1-n DICOM/DICONDE (0014,40A1) LO ImageQualityIndicatorMaterial 1-n DICOM/DICONDE (0014,40A2) LO ImageQualityIndicatorSize 1-n DICOM/DICONDE +(0014,4101) SQ WaveDimensionsDefinitionSequence 1 DICOM/DICONDE +(0014,4102) US WaveDimensionNumber 1 DICOM/DICONDE +(0014,4103) LO WaveDimensionDescription 1 DICOM/DICONDE +(0014,4104) US WaveDimensionUnit 1 DICOM/DICONDE +(0014,4105) CS WaveDimensionValueType 1 DICOM/DICONDE +(0014,4106) SQ WaveDimensionValuesSequence 1-n DICOM/DICONDE +(0014,4107) US ReferencedWaveDimension 1 DICOM/DICONDE +(0014,4108) SL IntegerNumericValue 1 DICOM/DICONDE +(0014,4109) OB ByteNumericValue 1 DICOM/DICONDE +(0014,410A) OW ShortNumericValue 1 DICOM/DICONDE +(0014,410B) OF SinglePrecisionFloatingPointNumericValue 1 DICOM/DICONDE +(0014,410C) OD DoublePrecisionFloatingPointNumericValue 1 DICOM/DICONDE (0014,5002) IS LINACEnergy 1 DICOM/DICONDE (0014,5004) IS LINACOutput 1 DICOM/DICONDE (0014,5100) US ActiveAperture 1 DICOM/DICONDE @@ -2534,6 +2563,8 @@ (0040,A030) DT VerificationDateTime 1 DICOM (0040,A032) DT ObservationDateTime 1 DICOM (0040,A033) DT ObservationStartDateTime 1 DICOM +(0040,A034) DT EffectiveStartDateTime 1 DICOM +(0040,A035) DT EffectiveStopDateTime 1 DICOM (0040,A040) CS ValueType 1 DICOM (0040,A043) SQ ConceptNameCodeSequence 1 DICOM (0040,A050) CS ContinuityOfContent 1 DICOM @@ -2591,6 +2622,25 @@ (0040,A807) SQ TableColumnDefinitionSequence 1 DICOM (0040,A808) SQ CellValuesSequence 1 DICOM (0040,B020) SQ WaveformAnnotationSequence 1 DICOM +(0040,B030) SQ StructuredWaveformAnnotationSequence 1 DICOM +(0040,B031) SQ WaveformAnnotationDisplaySelectionSequence 1 DICOM +(0040,B032) US ReferencedMontageIndex 1 DICOM +(0040,B033) SQ WaveformTextualAnnotationSequence 1 DICOM +(0040,B034) DT AnnotationDateTime 1 DICOM +(0040,B035) SQ DisplayedWaveformSegmentSequence 1 DICOM +(0040,B036) DT SegmentDefinitionDateTime 1 DICOM +(0040,B037) SQ MontageActivationSequence 1 DICOM +(0040,B038) DS MontageActivationTimeOffset 1 DICOM +(0040,B039) SQ WaveformMontageSequence 1 DICOM +(0040,B03A) IS ReferencedMontageChannelNumber 1 DICOM +(0040,B03B) LT MontageName 1 DICOM +(0040,B03C) SQ MontageChannelSequence 1 DICOM +(0040,B03D) US MontageIndex 1 DICOM +(0040,B03E) IS MontageChannelNumber 1 DICOM +(0040,B03F) LO MontageChannelLabel 1 DICOM +(0040,B040) SQ MontageChannelSourceCodeSequence 1 DICOM +(0040,B041) SQ ContributingChannelSourcesSequence 1 DICOM +(0040,B042) FL ChannelWeight 1 DICOM (0040,DB00) CS TemplateIdentifier 1 DICOM (0040,DB73) UL ReferencedContentItemIdentifier 1-n DICOM (0040,E001) ST HL7InstanceIdentifier 1 DICOM @@ -2638,6 +2688,7 @@ (0044,0108) UI ReferencedAssertionUID 1 DICOM (0044,0109) SQ ApprovalSubjectSequence 1 DICOM (0044,010A) SQ OrganizationalRoleCodeSequence 1 DICOM +(0044,0110) SQ RTAssertionsSequence 1 DICOM (0046,0012) LO LensDescription 1 DICOM (0046,0014) SQ RightLensSequence 1 DICOM (0046,0015) SQ LeftLensSequence 1 DICOM @@ -2761,6 +2812,7 @@ (0048,0301) CS PixelOriginInterpretation 1 DICOM (0048,0302) UL NumberOfOpticalPaths 1 DICOM (0048,0303) UL TotalPixelMatrixFocalPlanes 1 DICOM +(0048,0304) CS TilesOverlap 1 DICOM (0050,0004) CS CalibrationImage 1 DICOM (0050,0010) SQ DeviceSequence 1 DICOM (0050,0012) SQ ContainerComponentTypeCodeSequence 1 DICOM @@ -3748,6 +3800,11 @@ (3004,0070) DS DVHMinimumDose 1 DICOM (3004,0072) DS DVHMaximumDose 1 DICOM (3004,0074) DS DVHMeanDose 1 DICOM +(3004,0080) SQ DoseCalculationModelSequence 1 DICOM +(3004,0081) SQ DoseCalculationAlgorithmSequence 1 DICOM +(3004,0082) CS CommissioningStatus 1 DICOM +(3004,0083) SQ DoseCalculationModelParameterSequence 1 DICOM +(3004,0084) CS DoseDepositionCalculationMedium 1 DICOM (3006,0002) SH StructureSetLabel 1 DICOM (3006,0004) LO StructureSetName 1 DICOM (3006,0006) ST StructureSetDescription 1 DICOM @@ -4257,6 +4314,8 @@ (300A,0398) FL ScanningSpotSize 2 DICOM (300A,0399) FL ScanSpotSizesDelivered 2-2n DICOM (300A,039A) IS NumberOfPaintings 1 DICOM +(300A,039B) FL ScanSpotGantryAngles 1-n DICOM +(300A,039C) FL ScanSpotPatientSupportAngles 1-n DICOM (300A,03A0) SQ IonToleranceTableSequence 1 DICOM (300A,03A2) SQ IonBeamSequence 1 DICOM (300A,03A4) SQ IonBeamLimitingDeviceSequence 1 DICOM @@ -4897,6 +4956,7 @@ (0010,1000) LO RETIRED_OtherPatientIDs 1-n DICOM/retired (0010,1050) LO RETIRED_InsurancePlanIdentification 1-n DICOM/retired (0010,1090) LO RETIRED_MedicalRecordLocator 1 DICOM/retired +(0010,2160) SH RETIRED_EthnicGroup 1 DICOM/retired (0014,0023) ST RETIRED_CADFileFormat 1 DICOM/retired (0014,0024) ST RETIRED_ComponentReferenceSystem 1 DICOM/retired (0014,0045) ST RETIRED_MaterialPropertiesFileFormatRetired 1 DICOM/retired @@ -5326,7 +5386,6 @@ (7F00-7FFF,0020) OW RETIRED_VariableCoefficientsSDVN 1 DICOM/retired (7F00-7FFF,0030) OW RETIRED_VariableCoefficientsSDHN 1 DICOM/retired (7F00-7FFF,0040) OW RETIRED_VariableCoefficientsSDDN 1 DICOM/retired -(7F00-7FFF,0040) OW RETIRED_VariableCoefficientsSDDN 1 DICOM/retired # #--------------------------------------------------------------------------- # diff --git a/dcmdata/docs/cda2dcm.man b/dcmdata/docs/cda2dcm.man index 799dda19..074bbc60 100644 --- a/dcmdata/docs/cda2dcm.man +++ b/dcmdata/docs/cda2dcm.man @@ -14,269 +14,15 @@ cda2dcm [options] cdafile-in dcmfile-out \section cda2dcm_description DESCRIPTION -The \b cda2dcm utility reads a CDA file (\e cdafile-in), converts it to a -DICOM Encapsulated CDA Storage SOP instance and stores the converted data -to an output file (\e dcmfile-out). +The \b cda2dcm tool is deprecated. Use \b dcmencap instead, which supports +the same command line parameters, and more. -\section cda2dcm_parameters PARAMETERS +\section cda2dcm_see_also SEE ALSO -\verbatim -cdafile-in CDA input filename to be encapsulated - -dcmfile-out DICOM output filename ("-" for stdout) -\endverbatim - -\section cda2dcm_options OPTIONS - -\subsection cda2dcm_general_options general options -\verbatim - -h --help - print this help text and exit - - --version - print version information and exit - - --arguments - print expanded command line arguments - - -q --quiet - quiet mode, print no warnings and errors - - -v --verbose - verbose mode, print processing details - - -d --debug - debug mode, print debug information - - -ll --log-level [l]evel: string constant - (fatal, error, warn, info, debug, trace) - use level l for the logger - - -lc --log-config [f]ilename: string - use config file f for the logger -\endverbatim - -\subsection cda2dcm_dicom_document_options DICOM document options -\verbatim -document title: - - +t --title [t]itle: string (default: empty) - document title - - +cn --concept-name [CSD] [CV] [CM]: string (default: empty) - coded representation of document title defined by coding - scheme designator CSD, code value CV and code meaning CM - -patient data: - - +pn --patient-name [n]ame: string - patient's name in DICOM PN syntax - - +pi --patient-id [i]d: string - patient identifier - - +pb --patient-birthdate [d]ate: string (YYYYMMDD) - patient's birth date - - +ps --patient-sex [s]ex: string (M, F or O) - patient's sex - -study and series: - - +sg --generate - generate new study and series UIDs (default) - - +st --study-from [f]ilename: string - read patient/study data from DICOM file - - +se --series-from [f]ilename: string - read patient/study/series data from DICOM file - -instance number: - - +i1 --instance-one - use instance number 1 (default, not with +se) - - +ii --instance-inc - increment instance number (only with +se) - - +is --instance-set [i]nstance number: integer - use instance number i - -burned-in annotation: - - +an --annotation-yes - document contains patient identifying data (default) - - -an --annotation-no - document does not contain patient identifying data - -override CDA file data: - - -ov --no-override - CDA patient and document data must match study, - series or manually entered information (default) - - +ov --override - data obtained from the CDA file will be overwritten - by study, series, or manually entered information -\endverbatim - -\subsection cda2dcm_processing_options processing options -\verbatim -other processing options: - - -k --key [k]ey: gggg,eeee="str", path or dictionary name="str" - add further attribute -\endverbatim - -\subsection cda2dcm_output_options output options -\verbatim -output file format: - - +F --write-file - write file format (default) - - -F --write-dataset - write data set without file meta information - -group length encoding: - - +g= --group-length-recalc - recalculate group lengths if present (default) - - +g --group-length-create - always write with group length elements - - -g --group-length-remove - always write without group length elements - -length encoding in sequences and items: - - +e --length-explicit - write with explicit lengths (default) - - -e --length-undefined - write with undefined lengths - -data set trailing padding (not with --write-dataset): - - -p --padding-off - no padding (implicit if --write-dataset) - - +p --padding-create [f]ile-pad [i]tem-pad: integer - align file on multiple of f bytes - and items on multiple of i bytes -\endverbatim - -\section cda2dcm_notes NOTES - -\subsection cda2dcm_attribute_sources Attribute Sources - -The application may be fed with some additional input for filling mandatory (and -optional) attributes in the new DICOM file like patient, study and series -information: - -- The \e --key option can be used to add further attributes to the DICOM output - file. - -- It is also possible to specify sequences, items and nested attributes using - the \e --key option. In these cases, a special "path" notation has to be - used. Details on this path notation can be found in the documentation of - \b dcmodify. - -- The \e --key option can be present more than once. - -- The value part (after the '=') may be absent causing the attribute to be set - with zero length. - -- Please be advised that the \e --key option is applied at the very end, just - before saving the DICOM file, so there is no value checking whatsoever. - -\section cda2dcm_logging LOGGING - -The level of logging output of the various command line tools and underlying -libraries can be specified by the user. By default, only errors and warnings -are written to the standard error stream. Using option \e --verbose also -informational messages like processing details are reported. Option -\e --debug can be used to get more details on the internal activity, e.g. for -debugging purposes. Other logging levels can be selected using option -\e --log-level. In \e --quiet mode only fatal errors are reported. In such -very severe error events, the application will usually terminate. For more -details on the different logging levels, see documentation of module "oflog". - -In case the logging output should be written to file (optionally with logfile -rotation), to syslog (Unix) or the event log (Windows) option \e --log-config -can be used. This configuration file also allows for directing only certain -messages to a particular output stream and for filtering certain messages -based on the module or application where they are generated. An example -configuration file is provided in \/logger.cfg. - -\section cda2dcm_command_line COMMAND LINE - -All command line tools use the following notation for parameters: square -brackets enclose optional values (0-1), three trailing dots indicate that -multiple values are allowed (1-n), a combination of both means 0 to n values. - -Command line options are distinguished from parameters by a leading '+' or '-' -sign, respectively. Usually, order and position of command line options are -arbitrary (i.e. they can appear anywhere). However, if options are mutually -exclusive the rightmost appearance is used. This behavior conforms to the -standard evaluation rules of common Unix shells. - -In addition, one or more command files can be specified using an '@' sign as a -prefix to the filename (e.g. \@command.txt). Such a command argument -is replaced by the content of the corresponding text file (multiple -whitespaces are treated as a single separator unless they appear between two -quotation marks) prior to any further evaluation. Please note that a command -file cannot contain another command file. This simple but effective approach -allows one to summarize common combinations of options/parameters and avoids -longish and confusing command lines (an example is provided in file -\/dumppat.txt). - -\section cda2dcm_exit_codes EXIT CODES - -The \b cda2dcm utility uses the following exit codes when terminating. This -enables the user to check for the reason why the application terminated. - -\subsection cda2dcm_exit_codes_general general -\verbatim -EXITCODE_NO_ERROR 0 -EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 -EXITCODE_MEMORY_EXHAUSTED 4 -\endverbatim - -\subsection cda2dcm_exit_codes_input_file_errors input file errors -\verbatim -EXITCODE_CANNOT_READ_INPUT_FILE 20 -EXITCODE_NO_INPUT_FILES 21 -EXITCODE_INVALID_INPUT_FILE 22 -\endverbatim - -\subsection cda2dcm_exit_codes_output_file_errors output file errors -\verbatim -EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 -\endverbatim - -\section cda2dcm_environment ENVIRONMENT - -The \b cda2dcm utility will attempt to load DICOM data dictionaries specified -in the \e DCMDICTPATH environment variable. By default, i.e. if the -\e DCMDICTPATH environment variable is not set, the file -\/dicom.dic will be loaded unless the dictionary is built -into the application (default for Windows). - -The default behavior should be preferred and the \e DCMDICTPATH environment -variable only used when alternative data dictionaries are required. The -\e DCMDICTPATH environment variable has the same format as the Unix shell -\e PATH variable in that a colon (":") separates entries. On Windows systems, -a semicolon (";") is used as a separator. The data dictionary code will -attempt to load each file specified in the \e DCMDICTPATH environment -variable. -It is an error if no data dictionary can be loaded. +dcmencap(1) \section cda2dcm_copyright COPYRIGHT -Copyright (C) 2018-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2018-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/dcm2cda.man b/dcmdata/docs/dcm2cda.man index 7262d920..1a6cfcc7 100644 --- a/dcmdata/docs/dcm2cda.man +++ b/dcmdata/docs/dcm2cda.man @@ -14,206 +14,15 @@ dcm2cda [options] dcmfile-in cdafile-out \section dcm2cda_description DESCRIPTION -The \b dcm2cda utility reads a DICOM file of the Encapsulated CDA Storage SOP -Class (\e dcmfile-in), extracts the embedded CDA document and writes it to an -output file (\e cdafile-out). Optionally a command can be executed after the -creation of the CDA file. - -\section dcm2cda_parameters PARAMETERS - -\verbatim -dcmfile-in DICOM input filename ("-" for stdin) - -cdafile-out CDA output filename -\endverbatim - -\section dcm2cda_options OPTIONS - -\subsection dcm2cda_general_options general options -\verbatim - -h --help - print this help text and exit - - --version - print version information and exit - - --arguments - print expanded command line arguments - - -q --quiet - quiet mode, print no warnings and errors - - -v --verbose - verbose mode, print processing details - - -d --debug - debug mode, print debug information - - -ll --log-level [l]evel: string constant - (fatal, error, warn, info, debug, trace) - use level l for the logger - - -lc --log-config [f]ilename: string - use config file f for the logger -\endverbatim - -\subsection dcm2cda_input_options input options -\verbatim -input file format: - - +f --read-file - read file format or data set (default) - - +fo --read-file-only - read file format only - - -f --read-dataset - read data set without file meta information - -input transfer syntax: - - -t= --read-xfer-auto - use TS recognition (default) - - -td --read-xfer-detect - ignore TS specified in the file meta header - - -te --read-xfer-little - read with explicit VR little endian TS - - -tb --read-xfer-big - read with explicit VR big endian TS - - -ti --read-xfer-implicit - read with implicit VR little endian TS - -parsing of odd-length attributes: - - +ao --accept-odd-length - accept odd length attributes (default) - - +ae --assume-even-length - assume real length is one byte larger - -handling of undefined length UN elements: - - +ui --enable-cp246 - read undefined len UN as implicit VR (default) - - -ui --disable-cp246 - read undefined len UN as explicit VR - -handling of defined length UN elements: - - -uc --retain-un - retain elements as UN (default) - - +uc --convert-un - convert to real VR if known - -automatic data correction: - - +dc --enable-correction - enable automatic data correction (default) - - -dc --disable-correction - disable automatic data correction - -bitstream format of deflated input: - - +bd --bitstream-deflated - expect deflated bitstream (default) - - +bz --bitstream-zlib - expect deflated zlib bitstream -\endverbatim - -\section dcm2cda_logging LOGGING - -The level of logging output of the various command line tools and underlying -libraries can be specified by the user. By default, only errors and warnings -are written to the standard error stream. Using option \e --verbose also -informational messages like processing details are reported. Option -\e --debug can be used to get more details on the internal activity, e.g. for -debugging purposes. Other logging levels can be selected using option -\e --log-level. In \e --quiet mode only fatal errors are reported. In such -very severe error events, the application will usually terminate. For more -details on the different logging levels, see documentation of module "oflog". - -In case the logging output should be written to file (optionally with logfile -rotation), to syslog (Unix) or the event log (Windows) option \e --log-config -can be used. This configuration file also allows for directing only certain -messages to a particular output stream and for filtering certain messages -based on the module or application where they are generated. An example -configuration file is provided in \/logger.cfg. - -\section dcm2cda_exit_codes EXIT CODES - -The \b dcm2cda utility uses the following exit codes when terminating. This -enables the user to check for the reason why the application terminated. - -\subsection dcm2cda_exit_codes_general general -\verbatim -EXITCODE_NO_ERROR 0 -EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 -\endverbatim - -\subsection dcm2cda_exit_codes_input_file_errors input file errors -\verbatim -EXITCODE_CANNOT_READ_INPUT_FILE 20 -EXITCODE_NO_INPUT_FILES 21 -EXITCODE_INVALID_INPUT_FILE 22 -\endverbatim - -\subsection dcm2cda_exit_codes_output_file_errors output file errors -\verbatim -EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 -\endverbatim - -\section dcm2cda_command_line COMMAND LINE - -All command line tools use the following notation for parameters: square -brackets enclose optional values (0-1), three trailing dots indicate that -multiple values are allowed (1-n), a combination of both means 0 to n values. - -Command line options are distinguished from parameters by a leading '+' or '-' -sign, respectively. Usually, order and position of command line options are -arbitrary (i.e. they can appear anywhere). However, if options are mutually -exclusive the rightmost appearance is used. This behavior conforms to the -standard evaluation rules of common Unix shells. - -In addition, one or more command files can be specified using an '@' sign as a -prefix to the filename (e.g. \@command.txt). Such a command argument -is replaced by the content of the corresponding text file (multiple -whitespaces are treated as a single separator unless they appear between two -quotation marks) prior to any further evaluation. Please note that a command -file cannot contain another command file. This simple but effective approach -allows one to summarize common combinations of options/parameters and avoids -longish and confusing command lines (an example is provided in file -\/dumppat.txt). - -\section dcm2cda_environment ENVIRONMENT - -The \b dcm2cda utility will attempt to load DICOM data dictionaries specified -in the \e DCMDICTPATH environment variable. By default, i.e. if the -\e DCMDICTPATH environment variable is not set, the file -\/dicom.dic will be loaded unless the dictionary is built -into the application (default for Windows). - -The default behavior should be preferred and the \e DCMDICTPATH environment -variable only used when alternative data dictionaries are required. The -\e DCMDICTPATH environment variable has the same format as the Unix shell -\e PATH variable in that a colon (":") separates entries. On Windows systems, -a semicolon (";") is used as a separator. The data dictionary code will -attempt to load each file specified in the \e DCMDICTPATH environment variable. -It is an error if no data dictionary can be loaded. +The \b dcm2cda tool is deprecated. Use \b dcmdecap instead, which supports +the same command line parameters, and more. \section dcm2cda_see_also SEE ALSO -cda2dcm(1) +dcmdecap(1) \section dcm2cda_copyright COPYRIGHT -Copyright (C) 2023-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2023-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/dcm2json.man b/dcmdata/docs/dcm2json.man index 6d9c11ad..bcca01f8 100644 --- a/dcmdata/docs/dcm2json.man +++ b/dcmdata/docs/dcm2json.man @@ -115,6 +115,27 @@ encoding of IS and DS (integer/decimal string) elements: -is --is-ds-string always encode as string + +bulk data URI options: + + -b --bulk-disabled + write everything as inline binary (default) + + +b --bulk-enabled + write large attributes as bulk data + + +bz --bulk-size [s]ize: integer (default: 1) + use bulk data for attributes >= s kBytes + + +bp --bulk-uri-prefix [u]ri prefix: string + use prefix u when generating bulk data URIs (default: file URI) + + +bd --bulk-dir [d]irectory: string + write bulk data files to d (default: '.') + + +bs --bulk-subdir + create subdirectory for each SOP instance + (default: no subdirectory) \endverbatim \subsection dcm2json_output_options output options @@ -298,12 +319,50 @@ the following (see DICOM Part 18 Section F for details): \subsection dcm2json_bulk_data Bulk Data -Binary data, i.e. DICOM element values with Value Representations (VR) of OB -or OW, as well as OD, OF, OL, OV and UN values are always written as -"InlineBinary" (base64 encoding) to the JSON output. A future version of this -tool might optionally use a "BulkDataURI" instead, i.e. the WADO-RS URL of a -bulk data item that contains the element value. This would be particularly -useful for large amounts of data, such as pixel data. +By default, binary data, i.e. DICOM element values with Value Representations +(VR) of OB, OD, OF, OL, OV, OW, and UN values are written as "InlineBinary" +(base64 encoding) to the JSON output. Option \e --bulk-enabled causes binary +data as well as DS, FD, FL, IS, SV and UV to be replaced by "BulkDataURI" +values if the element value is larger than the cut-off threshold (default: 1 +kByte). The cut-off threshold can be specified with the \e --bulk-size option. +The element values themselves are written as files to the directory given by +the \e --bulk-dir option (default: current directory). The filename is based +on a SHA-256 checksum of the element value. By default, file URIs are +generated that point to the bulk directory. For production use, a URI prefix +for a WADO-RS service over which the element values can be retrieved should be +specified using the \e --bulk-uri-prefix option. This can be implemented by +configuring a web server that has read access to \b dcm2json's bulk directory. +Finally, the option \e --bulk-subdir will cause a separate subdirectory to be +created (and used in the bulk data URI) for each distinct SOP instance. + +Note that the JSON syntax for the representation of encapsulated pixel data +in "InlineBinary" form is unspecified in DICOM, as is the JSON representation +of encapsulated multi-frame pixel data. These DICOM files cannot be converted +to JSON using \b dcm2json. + +The file name extension for the bulk data files generated can be used to +determine the MIME type that should be returned by the WADO-RS server: + +\verbatim + Extension MIME Type + + .bin application/octet-stream + .jpeg image/jpeg + .dicom-rle image/dicom-rle + .jls image/jls + .jp2 image/jp2 + .jpx image/jpx + .jphc image/jphc + .jxl image/jxl + .mpeg video/mpeg + .mp4 video/mp4 + .H265 video/H265 +\endverbatim + +Finally, it should be noted that bulk data will be written "as is", i.e. +\b dcm2json will not attempt to validate or modify the element value in any +way. For example, \b dcm2json will not add a JFIF header in the case of JPEG +baseline compressed images, which some JPEG viewers may expect. \section dcm2json_notes NOTES @@ -322,8 +381,8 @@ parsers cannot process larger numbers. \subsection dcm2json_character_encoding Character Encoding As required by the DICOM JSON encoding, \b dcm2json always creates output in -Unicode UTF-8 encoding and converts DICOM datasets accordingly. If this is not -possible, for example because DCMTK has been compiled without character set +Unicode UTF-8 encoding and converts DICOM data sets accordingly. If this is +not possible, for example because DCMTK has been compiled without character set conversion support, an error is returned. \section dcm2json_logging LOGGING @@ -423,6 +482,6 @@ replace any built-in tables. \section dcm2json_copyright COPYRIGHT -Copyright (C) 2016-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2016-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/dcm2pdf.man b/dcmdata/docs/dcm2pdf.man index b0403771..d7aac949 100644 --- a/dcmdata/docs/dcm2pdf.man +++ b/dcmdata/docs/dcm2pdf.man @@ -14,228 +14,15 @@ dcm2pdf [options] dcmfile-in pdffile-out \section dcm2pdf_description DESCRIPTION -The \b dcm2pdf utility reads a DICOM file of the Encapsulated PDF Storage SOP -Class (\e dcmfile-in), extracts the embedded PDF document and writes it to an -output file (\e pdffile-out). Optionally a command can be executed after the -creation of the PDF file. - -\section dcm2pdf_parameters PARAMETERS - -\verbatim -dcmfile-in DICOM input filename ("-" for stdin) - -pdffile-out PDF output filename -\endverbatim - -\section dcm2pdf_options OPTIONS - -\subsection dcm2pdf_general_options general options -\verbatim - -h --help - print this help text and exit - - --version - print version information and exit - - --arguments - print expanded command line arguments - - -q --quiet - quiet mode, print no warnings and errors - - -v --verbose - verbose mode, print processing details - - -d --debug - debug mode, print debug information - - -ll --log-level [l]evel: string constant - (fatal, error, warn, info, debug, trace) - use level l for the logger - - -lc --log-config [f]ilename: string - use config file f for the logger -\endverbatim - -\subsection dcm2pdf_input_options input options -\verbatim -input file format: - - +f --read-file - read file format or data set (default) - - +fo --read-file-only - read file format only - - -f --read-dataset - read data set without file meta information - -input transfer syntax: - - -t= --read-xfer-auto - use TS recognition (default) - - -td --read-xfer-detect - ignore TS specified in the file meta header - - -te --read-xfer-little - read with explicit VR little endian TS - - -tb --read-xfer-big - read with explicit VR big endian TS - - -ti --read-xfer-implicit - read with implicit VR little endian TS - -parsing of odd-length attributes: - - +ao --accept-odd-length - accept odd length attributes (default) - - +ae --assume-even-length - assume real length is one byte larger - -handling of undefined length UN elements: - - +ui --enable-cp246 - read undefined len UN as implicit VR (default) - - -ui --disable-cp246 - read undefined len UN as explicit VR - -handling of defined length UN elements: - - -uc --retain-un - retain elements as UN (default) - - +uc --convert-un - convert to real VR if known - -automatic data correction: - - +dc --enable-correction - enable automatic data correction (default) - - -dc --disable-correction - disable automatic data correction - -bitstream format of deflated input: - - +bd --bitstream-deflated - expect deflated bitstream (default) - - +bz --bitstream-zlib - expect deflated zlib bitstream -\endverbatim - -\subsection dcm2pdf_processing_options processing options -execution options: -\verbatim - -x --exec [c]ommand: string - execute command c after PDF extraction -\endverbatim - -\section dcm2pdf_notes NOTES - -Option \e --exec allows for the execution of a certain command line after the -creation of the PDF document. The command line to be executed is passed to -this option as a parameter. The specified command line may contain the -placeholder '\#f', which will be replaced by the PDF filename at run time. -The specified command line is executed in the foreground, i.e. \b pdf2dcm will -be blocked until the command terminates. - -\section dcm2pdf_logging LOGGING - -The level of logging output of the various command line tools and underlying -libraries can be specified by the user. By default, only errors and warnings -are written to the standard error stream. Using option \e --verbose also -informational messages like processing details are reported. Option -\e --debug can be used to get more details on the internal activity, e.g. for -debugging purposes. Other logging levels can be selected using option -\e --log-level. In \e --quiet mode only fatal errors are reported. In such -very severe error events, the application will usually terminate. For more -details on the different logging levels, see documentation of module "oflog". - -In case the logging output should be written to file (optionally with logfile -rotation), to syslog (Unix) or the event log (Windows) option \e --log-config -can be used. This configuration file also allows for directing only certain -messages to a particular output stream and for filtering certain messages -based on the module or application where they are generated. An example -configuration file is provided in \/logger.cfg. - -\section dcm2pdf_exit_codes EXIT CODES - -The \b dcm2pdf utility uses the following exit codes when terminating. This -enables the user to check for the reason why the application terminated. - -\subsection dcm2cda_exit_codes_general general -\verbatim -EXITCODE_NO_ERROR 0 -EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 -\endverbatim - -\subsection dcm2cda_exit_codes_input_file_errors input file errors -\verbatim -EXITCODE_CANNOT_READ_INPUT_FILE 20 -EXITCODE_NO_INPUT_FILES 21 -EXITCODE_INVALID_INPUT_FILE 22 -\endverbatim - -\subsection dcm2cda_exit_codes_output_file_errors output file errors -\verbatim -EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 -\endverbatim - -\subsection dcm2cda_exit_codes_processing_errors processing errors -\verbatim -EXITCODE_CANNOT_CONVERT_TO_UNICODE 80 -EXITCODE_CANNOT_WRITE_VALID_JSON 81 -\endverbatim - -\section dcm2pdf_command_line COMMAND LINE - -All command line tools use the following notation for parameters: square -brackets enclose optional values (0-1), three trailing dots indicate that -multiple values are allowed (1-n), a combination of both means 0 to n values. - -Command line options are distinguished from parameters by a leading '+' or '-' -sign, respectively. Usually, order and position of command line options are -arbitrary (i.e. they can appear anywhere). However, if options are mutually -exclusive the rightmost appearance is used. This behavior conforms to the -standard evaluation rules of common Unix shells. - -In addition, one or more command files can be specified using an '@' sign as a -prefix to the filename (e.g. \@command.txt). Such a command argument -is replaced by the content of the corresponding text file (multiple -whitespaces are treated as a single separator unless they appear between two -quotation marks) prior to any further evaluation. Please note that a command -file cannot contain another command file. This simple but effective approach -allows one to summarize common combinations of options/parameters and avoids -longish and confusing command lines (an example is provided in file -\/dumppat.txt). - -\section dcm2pdf_environment ENVIRONMENT - -The \b dcm2pdf utility will attempt to load DICOM data dictionaries specified -in the \e DCMDICTPATH environment variable. By default, i.e. if the -\e DCMDICTPATH environment variable is not set, the file -\/dicom.dic will be loaded unless the dictionary is built -into the application (default for Windows). - -The default behavior should be preferred and the \e DCMDICTPATH environment -variable only used when alternative data dictionaries are required. The -\e DCMDICTPATH environment variable has the same format as the Unix shell -\e PATH variable in that a colon (":") separates entries. On Windows systems, -a semicolon (";") is used as a separator. The data dictionary code will -attempt to load each file specified in the \e DCMDICTPATH environment variable. -It is an error if no data dictionary can be loaded. +The \b dcm2pdf tool is deprecated. Use \b dcmdecap instead, which supports +the same command line parameters, and more. \section dcm2pdf_see_also SEE ALSO -pdf2dcm(1) +dcmdecap(1) \section dcm2pdf_copyright COPYRIGHT -Copyright (C) 2007-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2007-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/dcm2xml.man b/dcmdata/docs/dcm2xml.man index a415951d..ef776b1f 100644 --- a/dcmdata/docs/dcm2xml.man +++ b/dcmdata/docs/dcm2xml.man @@ -405,6 +405,6 @@ replace any built-in tables. \section dcm2xml_copyright COPYRIGHT -Copyright (C) 2002-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2002-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/dcmconv.man b/dcmdata/docs/dcmconv.man index e6d5ea60..87fdd7b4 100644 --- a/dcmdata/docs/dcmconv.man +++ b/dcmdata/docs/dcmconv.man @@ -397,6 +397,6 @@ replace any built-in tables. \section dcmconv_copyright COPYRIGHT -Copyright (C) 1994-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1994-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/dcmcrle.man b/dcmdata/docs/dcmcrle.man index f35402f8..4024ff71 100644 --- a/dcmdata/docs/dcmcrle.man +++ b/dcmdata/docs/dcmcrle.man @@ -258,6 +258,6 @@ It is an error if no data dictionary can be loaded. \section dcmcrle_copyright COPYRIGHT -Copyright (C) 2002-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2002-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/dcmdata.dox b/dcmdata/docs/dcmdata.dox index ab99717c..5bc8a5a4 100644 --- a/dcmdata/docs/dcmdata.dox +++ b/dcmdata/docs/dcmdata.dox @@ -26,13 +26,16 @@ This module contains the following command line tools: \li \ref dcm2xml \li \ref dcmconv \li \ref dcmcrle +\li \ref dcmdecap \li \ref dcmdrle \li \ref dcmdump +\li \ref dcmencap \li \ref dcmftest \li \ref dcmgpdir \li \ref dcmodify \li \ref dump2dcm \li \ref img2dcm +\li \ref json2dcm \li \ref pdf2dcm \li \ref stl2dcm \li \ref xml2dcm diff --git a/dcmdata/docs/dcmdecap.man b/dcmdata/docs/dcmdecap.man new file mode 100644 index 00000000..cbfbc9af --- /dev/null +++ b/dcmdata/docs/dcmdecap.man @@ -0,0 +1,240 @@ +/*! + +\if MANPAGES +\page dcmdecap Extract encapsulated file from DICOM encapsulated storage object +\else +\page dcmdecap dcmdecap: Extract encapsulated file from DICOM encapsulated storage object +\endif + +\section dcmdecap_synopsis SYNOPSIS + +\verbatim +dcmdecap [options] dcmfile-in encfile-out +\endverbatim + +\section dcmdecap_description DESCRIPTION + +The \b dcmdecap utility reads a DICOM file of one of the Encapsulated Storage SOP +Classes (\e dcmfile-in), extracts the embedded document and writes it to an +output file (\e encfile-out). Optionally a command can be executed after the +creation of the output file. + +\section dcmdecap_parameters PARAMETERS + +\verbatim +dcmfile-in DICOM input filename ("-" for stdin) + +encfile-out Encapsulated document output filename ("-" for stdout) +\endverbatim + +\section dcmdecap_options OPTIONS + +\subsection dcmdecap_general_options general options +\verbatim + -h --help + print this help text and exit + + --version + print version information and exit + + --arguments + print expanded command line arguments + + -q --quiet + quiet mode, print no warnings and errors + + -v --verbose + verbose mode, print processing details + + -d --debug + debug mode, print debug information + + -ll --log-level [l]evel: string constant + (fatal, error, warn, info, debug, trace) + use level l for the logger + + -lc --log-config [f]ilename: string + use config file f for the logger +\endverbatim + +\subsection dcmdecap_input_options input options +\verbatim +input file format: + + +f --read-file + read file format or data set (default) + + +fo --read-file-only + read file format only + + -f --read-dataset + read data set without file meta information + +input transfer syntax: + + -t= --read-xfer-auto + use TS recognition (default) + + -td --read-xfer-detect + ignore TS specified in the file meta header + + -te --read-xfer-little + read with explicit VR little endian TS + + -tb --read-xfer-big + read with explicit VR big endian TS + + -ti --read-xfer-implicit + read with implicit VR little endian TS + +parsing of odd-length attributes: + + +ao --accept-odd-length + accept odd length attributes (default) + + +ae --assume-even-length + assume real length is one byte larger + +handling of undefined length UN elements: + + +ui --enable-cp246 + read undefined len UN as implicit VR (default) + + -ui --disable-cp246 + read undefined len UN as explicit VR + +handling of defined length UN elements: + + -uc --retain-un + retain elements as UN (default) + + +uc --convert-un + convert to real VR if known + +automatic data correction: + + +dc --enable-correction + enable automatic data correction (default) + + -dc --disable-correction + disable automatic data correction + +bitstream format of deflated input: + + +bd --bitstream-deflated + expect deflated bitstream (default) + + +bz --bitstream-zlib + expect deflated zlib bitstream +\endverbatim + +\subsection dcmdecap_processing_options processing options +execution options: +\verbatim + -x --exec [c]ommand: string + execute command c after document extraction +\endverbatim + +\section dcmdecap_notes NOTES + +Option \e --exec allows for the execution of a certain command line after the +creation of the PDF document. The command line to be executed is passed to +this option as a parameter. The specified command line may contain the +placeholder '\#f', which will be replaced by the output filename at run time. +The specified command line is executed in the foreground, i.e. \b dcmdecap will +be blocked until the command terminates. + +\section dcmdecap_logging LOGGING + +The level of logging output of the various command line tools and underlying +libraries can be specified by the user. By default, only errors and warnings +are written to the standard error stream. Using option \e --verbose also +informational messages like processing details are reported. Option +\e --debug can be used to get more details on the internal activity, e.g. for +debugging purposes. Other logging levels can be selected using option +\e --log-level. In \e --quiet mode only fatal errors are reported. In such +very severe error events, the application will usually terminate. For more +details on the different logging levels, see documentation of module "oflog". + +In case the logging output should be written to file (optionally with logfile +rotation), to syslog (Unix) or the event log (Windows) option \e --log-config +can be used. This configuration file also allows for directing only certain +messages to a particular output stream and for filtering certain messages +based on the module or application where they are generated. An example +configuration file is provided in \/logger.cfg. + +\section dcmdecap_exit_codes EXIT CODES + +The \b dcmdecap utility uses the following exit codes when terminating. This +enables the user to check for the reason why the application terminated. + +\subsection dcmdecap_exit_codes_general general +\verbatim +EXITCODE_NO_ERROR 0 +EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 +\endverbatim + +\subsection dcmdecap_exit_codes_input_file_errors input file errors +\verbatim +EXITCODE_CANNOT_READ_INPUT_FILE 20 +EXITCODE_NO_INPUT_FILES 21 +EXITCODE_INVALID_INPUT_FILE 22 +\endverbatim + +\subsection dcmdecap_exit_codes_output_file_errors output file errors +\verbatim +EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 +\endverbatim + +\subsection dcmdecap_exit_codes_processing_errors processing errors +\verbatim +EXITCODE_EXEC_FAILED 91 +\endverbatim + +\section dcmdecap_command_line COMMAND LINE + +All command line tools use the following notation for parameters: square +brackets enclose optional values (0-1), three trailing dots indicate that +multiple values are allowed (1-n), a combination of both means 0 to n values. + +Command line options are distinguished from parameters by a leading '+' or '-' +sign, respectively. Usually, order and position of command line options are +arbitrary (i.e. they can appear anywhere). However, if options are mutually +exclusive the rightmost appearance is used. This behavior conforms to the +standard evaluation rules of common Unix shells. + +In addition, one or more command files can be specified using an '@' sign as a +prefix to the filename (e.g. \@command.txt). Such a command argument +is replaced by the content of the corresponding text file (multiple +whitespaces are treated as a single separator unless they appear between two +quotation marks) prior to any further evaluation. Please note that a command +file cannot contain another command file. This simple but effective approach +allows one to summarize common combinations of options/parameters and avoids +longish and confusing command lines (an example is provided in file +\/dumppat.txt). + +\section dcmdecap_environment ENVIRONMENT + +The \b dcmdecap utility will attempt to load DICOM data dictionaries specified +in the \e DCMDICTPATH environment variable. By default, i.e. if the +\e DCMDICTPATH environment variable is not set, the file +\/dicom.dic will be loaded unless the dictionary is built +into the application (default for Windows). + +The default behavior should be preferred and the \e DCMDICTPATH environment +variable only used when alternative data dictionaries are required. The +\e DCMDICTPATH environment variable has the same format as the Unix shell +\e PATH variable in that a colon (":") separates entries. On Windows systems, +a semicolon (";") is used as a separator. The data dictionary code will +attempt to load each file specified in the \e DCMDICTPATH environment variable. +It is an error if no data dictionary can be loaded. + +\section dcmdecap_see_also SEE ALSO + +dcmencap(1) + +\section dcmdecap_copyright COPYRIGHT + +Copyright (C) 2007-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. + +*/ diff --git a/dcmdata/docs/dcmdrle.man b/dcmdata/docs/dcmdrle.man index 65f8cc9c..395886ee 100644 --- a/dcmdata/docs/dcmdrle.man +++ b/dcmdata/docs/dcmdrle.man @@ -247,6 +247,6 @@ It is an error if no data dictionary can be loaded. \section dcmdrle_copyright COPYRIGHT -Copyright (C) 2002-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany +Copyright (C) 2002-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany */ diff --git a/dcmdata/docs/dcmdump.man b/dcmdata/docs/dcmdump.man index a1ee447c..8edd7af5 100644 --- a/dcmdata/docs/dcmdump.man +++ b/dcmdata/docs/dcmdump.man @@ -429,6 +429,6 @@ replace any built-in tables. \section dcmdump_copyright COPYRIGHT -Copyright (C) 1994-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1994-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/dcmencap.man b/dcmdata/docs/dcmencap.man new file mode 100644 index 00000000..589aab18 --- /dev/null +++ b/dcmdata/docs/dcmencap.man @@ -0,0 +1,320 @@ +/*! + +\if MANPAGES +\page dcmencap Encapsulate document into DICOM format +\else +\page dcmencap dcmencap: Encapsulate document into DICOM format +\endif + +\section dcmencap_synopsis SYNOPSIS + +\verbatim +dcmencap [options] docfile-in dcmfile-out +\endverbatim + +\section dcmencap_description DESCRIPTION + +The \b dcmencap utility reads a document file in one of the supported file formats, +converts it to a SOP instance of the corresponding DICOM Encapsulated Storage SOP Class +and stores the converted data in an output file (\e dcmfile-out). + +\section dcmencap_parameters PARAMETERS + +\verbatim +docfile-in input filename to be converted + +dcmfile-out DICOM output filename ("-" for stdout) +\endverbatim + +\section dcmencap_options OPTIONS + +\subsection dcmencap_general_options general options +\verbatim + -h --help + print this help text and exit + + --version + print version information and exit + + --arguments + print expanded command line arguments + + -q --quiet + quiet mode, print no warnings and errors + + -v --verbose + verbose mode, print processing details + + -d --debug + debug mode, print debug information + + -ll --log-level [l]evel: string constant + (fatal, error, warn, info, debug, trace) + use level l for the logger + + -lc --log-config [f]ilename: string + use config file f for the logger +\endverbatim + +\subsection dcmencap_input_options input options +\verbatim +input file format: + + +fa --filetype-auto + automatically determine file type (default) + + +fp --filetype-pdf + expect PDF file + + +fc --filetype-cda + expect CDA file + + +fs --filetype-stl + expect STL file + + +fm --filetype-mtl + expect MTL file + + +fo --filetype-obj + expect OBJ file +\endverbatim + +\subsection dcmencap_dicom_document_options DICOM document options +\verbatim +document title: + + +t --title [t]itle: string (default: empty) + document title + + +cn --concept-name [CSD] [CV] [CM]: string (default: empty) + coded representation of document title defined by coding + scheme designator CSD, code value CV and code meaning CM + +patient data: + + +pn --patient-name [n]ame: string + patient's name in DICOM PN syntax + + +pi --patient-id [i]d: string + patient identifier + + +pb --patient-birthdate [d]ate: string (YYYYMMDD) + patient's birth date + + +ps --patient-sex [s]ex: string (M, F or O) + patient's sex + +device data: + + +mn --manufacturer [n]ame: string + manufacturer's name + + +mm --manufacturer-model [n]ame: string + manufacturer's model name + + +ds --device-serial [n]umber: string + device serial number + + +sv --software-versions [v]ersions: string + software versions + +manufacturing 3d model data (STL/MTL/OBJ only): + + +mu --measurement-units [CSD] [CV] [CM]: string + measurement units with coding scheme designator CSD, + code value CV and code meaning CM (default: UCUM, um, um) + +study and series: + + +sg --generate + generate new study and series UIDs (default) + + +st --study-from [f]ilename: string + read patient/study data from DICOM file + + +se --series-from [f]ilename: string + read patient/study/series data from DICOM file + +instance number: + + +i1 --instance-one + use instance number 1 (default, not with +se) + + +ii --instance-inc + increment instance number (only with +se) + + +is --instance-set [i]nstance number: integer + use instance number i + +burned-in annotation: + + +an --annotation-yes + document contains patient identifying data (default) + + -an --annotation-no + document does not contain patient identifying data +\endverbatim + +\subsection dcmencap_processing_options processing options +\verbatim +CDA processing options: + + -ov --no-override + CDA patient and document data must match study, + series or manually entered information (default) + + +ov --override + CDA's data will be overwritten by study, series + or manually entered information + +other processing options: + + -k --key [k]ey: gggg,eeee="str", path or dictionary name="str" + add further attribute +\endverbatim + +\subsection dcmencap_output_options output options +\verbatim +output transfer syntax: + +te --write-xfer-little + write with explicit VR little endian (default) + + +tb --write-xfer-big + write with explicit VR big endian TS + + +ti --write-xfer-implicit + write with implicit VR little endian TS + +group length encoding: + + -g --group-length-remove + write without group length elements (default) + + +g --group-length-create + write with group length elements + +length encoding in sequences and items: + + +e --length-explicit + write with explicit lengths (default) + + -e --length-undefined + write with undefined lengths + +data set trailing padding (not with --write-dataset): + + -p --padding-off + no padding (implicit if --write-dataset) + + +p --padding-create [f]ile-pad [i]tem-pad: integer + align file on multiple of f bytes + and items on multiple of i bytes +\endverbatim + +\section dcmencap_notes NOTES + +\subsection dcmencap_attribute_sources Attribute Sources + +The application may be fed with some additional input for filling mandatory +(and optional) attributes in the new DICOM file like patient, study and series +information: + +- The \e --key option can be used to add further attributes to the DICOM output + file. + +- It is also possible to specify sequences, items and nested attributes using + the \e --key option. In these cases, a special "path" notation has to be + used. Details on this path notation can be found in the documentation of + \b dcmodify. + +- The \e --key option can be present more than once. + +- The value part (after the '=') may be absent causing the attribute to be set + with zero length. + +- Please be advised that the \e --key option is applied at the very end, just + before saving the DICOM file, so there is no value checking whatsoever. + +\section dcmencap_logging LOGGING + +The level of logging output of the various command line tools and underlying +libraries can be specified by the user. By default, only errors and warnings +are written to the standard error stream. Using option \e --verbose also +informational messages like processing details are reported. Option +\e --debug can be used to get more details on the internal activity, e.g. for +debugging purposes. Other logging levels can be selected using option +\e --log-level. In \e --quiet mode only fatal errors are reported. In such +very severe error events, the application will usually terminate. For more +details on the different logging levels, see documentation of module "oflog". + +In case the logging output should be written to file (optionally with logfile +rotation), to syslog (Unix) or the event log (Windows) option \e --log-config +can be used. This configuration file also allows for directing only certain +messages to a particular output stream and for filtering certain messages +based on the module or application where they are generated. An example +configuration file is provided in \/logger.cfg. + +\section dcmencap_command_line COMMAND LINE + +All command line tools use the following notation for parameters: square +brackets enclose optional values (0-1), three trailing dots indicate that +multiple values are allowed (1-n), a combination of both means 0 to n values. + +Command line options are distinguished from parameters by a leading '+' or '-' +sign, respectively. Usually, order and position of command line options are +arbitrary (i.e. they can appear anywhere). However, if options are mutually +exclusive the rightmost appearance is used. This behavior conforms to the +standard evaluation rules of common Unix shells. + +In addition, one or more command files can be specified using an '@' sign as a +prefix to the filename (e.g. \@command.txt). Such a command argument +is replaced by the content of the corresponding text file (multiple +whitespaces are treated as a single separator unless they appear between two +quotation marks) prior to any further evaluation. Please note that a command +file cannot contain another command file. This simple but effective approach +allows one to summarize common combinations of options/parameters and avoids +longish and confusing command lines (an example is provided in file +\/dumppat.txt). + +\section dcmencap_exit_codes EXIT CODES + +The \b dcmencap utility uses the following exit codes when terminating. This +enables the user to check for the reason why the application terminated. + +\subsection dcmencap_exit_codes_general general +\verbatim +EXITCODE_NO_ERROR 0 +\endverbatim + +\subsection dcmencap_exit_codes_input_file_errors input file errors +\verbatim +EXITCODE_INVALID_INPUT_FILE 22 +\endverbatim + +\subsection dcmencap_exit_codes_output_file_errors output file errors +\verbatim +EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 +\endverbatim + +\section dcmencap_environment ENVIRONMENT + +The \b dcmencap utility will attempt to load DICOM data dictionaries specified +in the \e DCMDICTPATH environment variable. By default, i.e. if the +\e DCMDICTPATH environment variable is not set, the file +\/dicom.dic will be loaded unless the dictionary is built +into the application (default for Windows). + +The default behavior should be preferred and the \e DCMDICTPATH environment +variable only used when alternative data dictionaries are required. The +\e DCMDICTPATH environment variable has the same format as the Unix shell +\e PATH variable in that a colon (":") separates entries. On Windows systems, +a semicolon (";") is used as a separator. The data dictionary code will +attempt to load each file specified in the \e DCMDICTPATH environment +variable. +It is an error if no data dictionary can be loaded. + +\section dcmencap_copyright COPYRIGHT + +Copyright (C) 2018-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. + +*/ diff --git a/dcmdata/docs/dcmftest.man b/dcmdata/docs/dcmftest.man index 66231ced..bae86648 100644 --- a/dcmdata/docs/dcmftest.man +++ b/dcmdata/docs/dcmftest.man @@ -39,6 +39,6 @@ is intended for use in shell script programming. \section dcmftest_copyright COPYRIGHT -Copyright (C) 1997-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1997-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/dcmgpdir.man b/dcmdata/docs/dcmgpdir.man index ef262d7b..e54708c0 100644 --- a/dcmdata/docs/dcmgpdir.man +++ b/dcmdata/docs/dcmgpdir.man @@ -23,6 +23,6 @@ the same command line parameters, and more. \section dcmgpdir_copyright COPYRIGHT -Copyright (C) 1996-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1996-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/dcmodify.man b/dcmdata/docs/dcmodify.man index 9f670680..31a67573 100644 --- a/dcmdata/docs/dcmodify.man +++ b/dcmdata/docs/dcmodify.man @@ -589,6 +589,6 @@ It is an error if no data dictionary can be loaded. \section dcmodify_copyright COPYRIGHT -Copyright (C) 2003-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2003-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/dump2dcm.man b/dcmdata/docs/dump2dcm.man index 22ebefbe..6c604992 100644 --- a/dcmdata/docs/dump2dcm.man +++ b/dcmdata/docs/dump2dcm.man @@ -314,6 +314,6 @@ It is an error if no data dictionary can be loaded. \section dump2dcm_copyright COPYRIGHT -Copyright (C) 1996-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1996-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/img2dcm.man b/dcmdata/docs/img2dcm.man index 316432e8..20cd0863 100644 --- a/dcmdata/docs/img2dcm.man +++ b/dcmdata/docs/img2dcm.man @@ -429,7 +429,7 @@ However, if you want to keep APPn markers (e.g. APP8/HP color transform information, aka 'mrfx') inside the DICOM stream, the option \e --keep-appn does the trick. Pay attention that the plugin will check the actual color transform specified in the APP8/HP marker. Since DICOM does not allow any -color transform to be specified in the APP8 marker, only a value of `0` (no +color transform to be specified in the APP8 marker, only a value of 0 (no color transform) is accepted. \subsubsection img2dcm_bmp_input_plugin BMP Input Plugin @@ -581,6 +581,6 @@ Photography images \section img2dcm_copyright COPYRIGHT -Copyright (C) 2007-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2007-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/json2dcm.man b/dcmdata/docs/json2dcm.man new file mode 100644 index 00000000..9091992f --- /dev/null +++ b/dcmdata/docs/json2dcm.man @@ -0,0 +1,481 @@ +/*! + +\if MANPAGES +\page json2dcm Convert JSON document to DICOM file or data set +\else +\page json2dcm json2dcm: Convert JSON document to DICOM file or data set +\endif + +\section json2dcm_synopsis SYNOPSIS + +\verbatim +json2dcm [options] jsonfile-in dcmfile-out +\endverbatim + +\section json2dcm_description DESCRIPTION + +The \b json2dcm utility converts the contents of a JSON (JavaScript Object +Notation) document to a binary DICOM file or data set. The JSON document is +expected to conform to the "DICOM JSON Model" as defined in DICOM Part 18 +Section F. Such JSON files can be created, e.g., using the \b dcm2json tool. + +\section json2dcm_parameters PARAMETERS + +\verbatim +jsonfile-in JSON input filename to be converted ("-" for stdin) + +dcmfile-out DICOM output filename ("-" for stdout) +\endverbatim + +\section json2dcm_options OPTIONS + +\subsection json2dcm_general_options general options +\verbatim + -h --help + print this help text and exit + + --version + print version information and exit + + --arguments + print expanded command line arguments + + -q --quiet + quiet mode, print no warnings and errors + + -v --verbose + verbose mode, print processing details + + -d --debug + debug mode, print debug information + + -ll --log-level [l]evel: string constant + (fatal, error, warn, info, debug, trace) + use level l for the logger + + -lc --log-config [f]ilename: string + use config file f for the logger +\endverbatim + +\subsection json2dcm_input_options input options +\verbatim +input file format: + + +f --read-meta-info + read meta information if present (default) + + -f --ignore-meta-info + ignore file meta information +\endverbatim + +\subsection json2dcm_processing_options processing options +\verbatim +unique identifiers: + + +Ug --generate-new-uids + generate new Study/Series/SOP Instance UID + + -Uo --dont-overwrite-uids + do not overwrite existing UIDs (default) + + +Uo --overwrite-uids + overwrite existing UIDs + +bulkdata URI handling: + + +Bu --parse-bulkdata-uri + parse Bulkdata URIs (default) + + -Bu --ignore-bulkdata-uri + ignore Bulkdata URIs + + +Bd --add-bulkdata-dir [d]irectory: string + add d to list of permitted bulk data sources + +handling of arrays with multiple data sets: + + -ar --array-reject + reject multiple data sets (default) + + +as --array-select [n]umber: integer + select data set n from array + + +ar --array-sequence + store all data sets in private sequence +\endverbatim + +\subsection json2dcm_output_options output options +\verbatim +output file format: + + +F --write-file + write file format (default) + + -F --write-dataset + write data set without file meta information + + +Fu --update-meta-info + update particular file meta information + +output transfer syntax: + + +t= --write-xfer-same + write with same TS as input (default) + + +te --write-xfer-little + write with explicit VR little endian TS + + +tb --write-xfer-big + write with explicit VR big endian TS + + +ti --write-xfer-implicit + write with implicit VR little endian TS + + +td --write-xfer-deflated + write with deflated explicit VR little endian TS + +error handling: + + -E --stop-on-error + do not write if document is invalid (default) + + +E --ignore-errors + attempt to write even if document is invalid + +post-1993 value representations: + + +u --enable-new-vr + enable support for new VRs (UN/UT) (default) + + -u --disable-new-vr + disable support for new VRs, convert to OB + +length encoding in sequences and items: + + +e --length-explicit + write with explicit lengths (default) + + -e --length-undefined + write with undefined lengths + +charset handling: + + +c --charset-accept + write with the given charset in JSON (default) + + -c --charset-replace + replace the given charset in JSON with UTF-8 + +deflate compression level (only with --write-xfer-deflated): + + +cl --compression-level [l]evel: integer (default: 6) + 0=uncompressed, 1=fastest, 9=best compression +\endverbatim + +\section json2dcm_notes NOTES + +The basic structure of the JSON input expected looks like the following (see +DICOM Part 18 Section F for details): + +\verbatim +{ + "00080005": { + "vr": "CS", + "Value": [ + "ISO_IR 192" + ] + }, + "00080020": { + "vr": "DT", + "Value": [ + "20130409" + ] + }, + "00080030": { + "vr": "TM", + "Value": [ + "131600.0000" + ] + }, + "00080050": { + "vr": "SH", + "Value": [ + "11235813" + ] + }, + "00080056": { + "vr": "CS", + "Value": [ + "ONLINE" + ] + }, + "00080061": { + "vr": "CS", + "Value": [ + "CT", + "PET" + ] + }, + "00080090": { + "vr": "PN", + "Value": [ + { + "Alphabetic": "^Bob^^Dr." + } + ] + }, + "00081190": { + "vr": "UR", + "Value": [ + "http://wado.nema.org/studies/ + 1.2.392.200036.9116.2.2.2.1762893313.1029997326.945873" + ] + }, + "00090010": { + "vr": "LO", + "Value": [ + "Vendor A" + ] + }, + "00091002": { + "vr": "UN", + "InlineBinary": "z0x9c8v7" + }, + "00100010": { + "vr": "PN", + "Value": [ + { + "Alphabetic": "Wang^XiaoDong" + } + ] + }, + "00100020": { + "vr": "LO", + "Value": [ + "12345" + ] + }, + "00100021": { + "vr": "LO", + "Value": [ + "Hospital A" + ] + }, + "00100030": { + "vr": "DA", + "Value": [ + "19670701" + ] + }, + "00100040": { + "vr": "CS", + "Value": [ + "M" + ] + }, + "00101002": { + "vr": "SQ", + "Value": [ + { + "00100020": { + "vr": "LO", + "Value": [ + "54321" + ] + }, + "00100021": { + "vr": "LO", + "Value": [ + "Hospital B" + ] + } + }, + { + "00100020": { + "vr": "LO", + "Value": [ + "24680" + ] + }, + "00100021": { + "vr": "LO", + "Value": [ + "Hospital C" + ] + } + } + ] + }, + "0020000D": { + "vr": "UI", + "Value": [ + "1.2.392.200036.9116.2.2.2.1762893313.1029997326.945873" + ] + }, + "00200010": { + "vr": "SH", + "Value": [ + "11235813" + ] + }, + "00201206": { + "vr": "IS", + "Value": [ + 4 + ] + }, + "00201208": { + "vr": "IS", + "Value": [ + 942 + ] + } +} +\endverbatim + +\subsection json2dcm_character_encoding Character Encoding + +The JSON format only supports UTF-8 encoding. Thus the generated DICOM file +will also contain UTF-8 encoding. If the JSON file does not contain a specific +character set, or a specific character set other than "ISO_IR 192", a warning +will be issued. + +\subsection json2dcm_bulk_data Binary Data, Bulk Data, and Pixel Data + +The DICOM JSON Model uses "InlineBinary" to store attribute values of binary +value representations such as "OB", "OW", "OD", "OF", "OL", "OV" etc. +in Base64 encoded form. This is supported in \b json2dcm for all binary +attributes, including unencapsulated pixel data. + +The DICOM JSON Model also permits attribute values to be stored separately +from the JSON data set and to be referenced through a BulkDataURI. +This is supported for file URIs that reference files in the local filesystem. +\b json2dcm tool also supports the inofficial extension to the file URI +scheme generated by \e DCM4CHE, where parameters named "offset" and "length" +are appended to the file URI in order to refer to a specific part of a file. +HTTP URIs as well as URIs that identify another part in a MIME +multipart/related structure are not yet supported in \b json2dcm. +If the command line option \e --ignore-bulkdata-uri is specified, +then all bulk data URIs are ignored and attributes with bulk data +will be written with empty value. + +Finally, encapsulated (in particular, compressed) pixel data is not supported +by \b json2dcm because the syntax of the DICOM JSON Model for this specific +case is not defined in the DICOM standard yet. + +\subsection json2dcm_arrays Arrays of Data Sets + +The DICOM JSON Model uses a JSON array structure to return multiple data sets +in DICOMweb services such as WADO-RS or QIDO-RS. JSON arrays containing a +single DICOM data set are automatically recognized by \b json2dcm and treated +like a data set without the surrounding array structure. JSON arrays +containing multiple data sets are rejected by default. Alternatively, the +\e --array-select option can be used to select one data set from the array to +be converted. The \e --array-sequence option causes all data sets to be +written as sequence items into a single private sequence with attribute tag +(0009,1000). Such files, which are mostly intended for debugging purposes, can +be recognized by the private creator element (0009,0010), which has the value +"JSON2DCM_LIST_OF_DATASETS". + +\subsection json2dcm_trailing_commas Trailing Commas + +Trailing commas are not permitted in JSON, but \b json2dcm will still accept +such JSON data sets without warning or error message because they are handled +gracefully by the underlying JSON parser. Users should, therefore, not assume +that a JSON data set is valid just because \b json2dcm accepts it. This tool +is not designed as a validator for JSON or the DICOM JSON Model. + +\section json2dcm_logging LOGGING + +The level of logging output of the various command line tools and underlying +libraries can be specified by the user. By default, only errors and warnings +are written to the standard error stream. Using option \e --verbose also +informational messages like processing details are reported. Option +\e --debug can be used to get more details on the internal activity, e.g. for +debugging purposes. Other logging levels can be selected using option +\e --log-level. In \e --quiet mode only fatal errors are reported. In such +very severe error events, the application will usually terminate. For more +details on the different logging levels, see documentation of module "oflog". + +In case the logging output should be written to file (optionally with logfile +rotation), to syslog (Unix) or the event log (Windows) option \e --log-config +can be used. This configuration file also allows for directing only certain +messages to a particular output stream and for filtering certain messages +based on the module or application where they are generated. An example +configuration file is provided in \/logger.cfg. + +\section json2dcm_command_line COMMAND LINE + +All command line tools use the following notation for parameters: square +brackets enclose optional values (0-1), three trailing dots indicate that +multiple values are allowed (1-n), a combination of both means 0 to n values. + +Command line options are distinguished from parameters by a leading '+' or '-' +sign, respectively. Usually, order and position of command line options are +arbitrary (i.e. they can appear anywhere). However, if options are mutually +exclusive the rightmost appearance is used. This behavior conforms to the +standard evaluation rules of common Unix shells. + +In addition, one or more command files can be specified using an '@' sign as a +prefix to the filename (e.g. \@command.txt). Such a command argument +is replaced by the content of the corresponding text file (multiple +whitespaces are treated as a single separator unless they appear between two +quotation marks) prior to any further evaluation. Please note that a command +file cannot contain another command file. This simple but effective approach +allows one to summarize common combinations of options/parameters and avoids +longish and confusing command lines (an example is provided in file +\/dumppat.txt). + +\section json2dcm_exit_codes EXIT CODES + +The \b dcm2json utility uses the following exit codes when terminating. This +enables the user to check for the reason why the application terminated. + +\subsection json2dcm_exit_codes_general general +\verbatim +EXITCODE_NO_ERROR 0 +EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 +\endverbatim + +\subsection json2dcm_exit_codes_input_file_errors input file errors +\verbatim +EXITCODE_CANNOT_READ_INPUT_FILE 20 +\endverbatim + +\subsection json2dcm_exit_codes_output_file_errors output file errors +\verbatim +EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 +\endverbatim + +\subsection json2dcm_exit_codes_processing_errors processing errors +\verbatim +EXITCODE_INVALID_JSON_CONTENT 65 +EXITCODE_BULKDATA_URI_NOT_SUPPORTED 66 +\endverbatim + +\section json2dcm_environment ENVIRONMENT + +The \b json2dcm utility will attempt to load DICOM data dictionaries specified +in the \e DCMDICTPATH environment variable. By default, i.e. if the +\e DCMDICTPATH environment variable is not set, the file +\/dicom.dic will be loaded unless the dictionary is built +into the application (default for Windows). + +The default behavior should be preferred and the \e DCMDICTPATH environment +variable only used when alternative data dictionaries are required. The +\e DCMDICTPATH environment variable has the same format as the Unix shell +\e PATH variable in that a colon (":") separates entries. On Windows systems, +a semicolon (";") is used as a separator. The data dictionary code will +attempt to load each file specified in the \e DCMDICTPATH environment variable. +It is an error if no data dictionary can be loaded. + +\section json2dcm_see_also SEE ALSO + +dcm2json(1) +dump2dcm(2) + +\section json2dcm_copyright COPYRIGHT + +Copyright (C) 2024-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. + +*/ diff --git a/dcmdata/docs/pdf2dcm.man b/dcmdata/docs/pdf2dcm.man index e923cef1..ff989c63 100644 --- a/dcmdata/docs/pdf2dcm.man +++ b/dcmdata/docs/pdf2dcm.man @@ -14,263 +14,15 @@ pdf2dcm [options] pdffile-in dcmfile-out \section pdf2dcm_description DESCRIPTION -The \b pdf2dcm utility reads a PDF file (\e pdffile-in), converts it to a -DICOM Encapsulated PDF Storage SOP instance and stores the converted data -to an output file (\e dcmfile-out). - -\section pdf2dcm_parameters PARAMETERS - -\verbatim -pdffile-in PDF input filename to be encapsulated - -dcmfile-out DICOM output filename ("-" for stdout) -\endverbatim - -\section pdf2dcm_options OPTIONS - -\subsection pdf2dcm_general_options general options -\verbatim - -h --help - print this help text and exit - - --version - print version information and exit - - --arguments - print expanded command line arguments - - -q --quiet - quiet mode, print no warnings and errors - - -v --verbose - verbose mode, print processing details - - -d --debug - debug mode, print debug information - - -ll --log-level [l]evel: string constant - (fatal, error, warn, info, debug, trace) - use level l for the logger - - -lc --log-config [f]ilename: string - use config file f for the logger -\endverbatim - -\subsection pdf2dcm_dicom_document_options DICOM document options -\verbatim -document title: - - +t --title [t]itle: string (default: empty) - document title - - +cn --concept-name [CSD] [CV] [CM]: string (default: empty) - coded representation of document title defined by coding - scheme designator CSD, code value CV and code meaning CM - -patient data: - - +pn --patient-name [n]ame: string - patient's name in DICOM PN syntax - - +pi --patient-id [i]d: string - patient identifier - - +pb --patient-birthdate [d]ate: string (YYYYMMDD) - patient's birth date - - +ps --patient-sex [s]ex: string (M, F or O) - patient's sex - -study and series: - - +sg --generate - generate new study and series UIDs (default) - - +st --study-from [f]ilename: string - read patient/study data from DICOM file - - +se --series-from [f]ilename: string - read patient/study/series data from DICOM file - -instance number: - - +i1 --instance-one - use instance number 1 (default, not with +se) - - +ii --instance-inc - increment instance number (only with +se) - - +is --instance-set [i]nstance number: integer - use instance number i - -burned-in annotation: - - +an --annotation-yes - document contains patient identifying data (default) - - -an --annotation-no - document does not contain patient identifying data -\endverbatim - -\subsection pdf2dcm_processing_options processing options -\verbatim -other processing options: - - -k --key [k]ey: gggg,eeee="str", path or dictionary name="str" - add further attribute -\endverbatim - -\subsection pdf2dcm_output_options output options -\verbatim -output file format: - - +F --write-file - write file format (default) - - -F --write-dataset - write data set without file meta information - -group length encoding: - - +g= --group-length-recalc - recalculate group lengths if present (default) - - +g --group-length-create - always write with group length elements - - -g --group-length-remove - always write without group length elements - -length encoding in sequences and items: - - +e --length-explicit - write with explicit lengths (default) - - -e --length-undefined - write with undefined lengths - -data set trailing padding (not with --write-dataset): - - -p --padding-off - no padding (implicit if --write-dataset) - - +p --padding-create [f]ile-pad [i]tem-pad: integer - align file on multiple of f bytes - and items on multiple of i bytes -\endverbatim - -\section pdf2dcm_notes NOTES - -\subsection pdf2dcm_attribute_sources Attribute Sources - -The application may be fed with some additional input for filling mandatory -(and optional) attributes in the new DICOM file like patient, study and series -information: - -- The \e --key option can be used to add further attributes to the DICOM output - file. - -- It is also possible to specify sequences, items and nested attributes using - the \e --key option. In these cases, a special "path" notation has to be - used. Details on this path notation can be found in the documentation of - \b dcmodify. - -- The \e --key option can be present more than once. - -- The value part (after the '=') may be absent causing the attribute to be set - with zero length. - -- Please be advised that the \e --key option is applied at the very end, just - before saving the DICOM file, so there is no value checking whatsoever. - -\section pdf2dcm_logging LOGGING - -The level of logging output of the various command line tools and underlying -libraries can be specified by the user. By default, only errors and warnings -are written to the standard error stream. Using option \e --verbose also -informational messages like processing details are reported. Option -\e --debug can be used to get more details on the internal activity, e.g. for -debugging purposes. Other logging levels can be selected using option -\e --log-level. In \e --quiet mode only fatal errors are reported. In such -very severe error events, the application will usually terminate. For more -details on the different logging levels, see documentation of module "oflog". - -In case the logging output should be written to file (optionally with logfile -rotation), to syslog (Unix) or the event log (Windows) option \e --log-config -can be used. This configuration file also allows for directing only certain -messages to a particular output stream and for filtering certain messages -based on the module or application where they are generated. An example -configuration file is provided in \/logger.cfg. - -\section pdf2dcm_command_line COMMAND LINE - -All command line tools use the following notation for parameters: square -brackets enclose optional values (0-1), three trailing dots indicate that -multiple values are allowed (1-n), a combination of both means 0 to n values. - -Command line options are distinguished from parameters by a leading '+' or '-' -sign, respectively. Usually, order and position of command line options are -arbitrary (i.e. they can appear anywhere). However, if options are mutually -exclusive the rightmost appearance is used. This behavior conforms to the -standard evaluation rules of common Unix shells. - -In addition, one or more command files can be specified using an '@' sign as a -prefix to the filename (e.g. \@command.txt). Such a command argument -is replaced by the content of the corresponding text file (multiple -whitespaces are treated as a single separator unless they appear between two -quotation marks) prior to any further evaluation. Please note that a command -file cannot contain another command file. This simple but effective approach -allows one to summarize common combinations of options/parameters and avoids -longish and confusing command lines (an example is provided in file -\/dumppat.txt). - -\section pdf2dcm_exit_codes EXIT CODES - -The \b pdf2dcm utility uses the following exit codes when terminating. This -enables the user to check for the reason why the application terminated. - -\subsection pdf2dcm_exit_codes_general general -\verbatim -EXITCODE_NO_ERROR 0 -EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 -EXITCODE_MEMORY_EXHAUSTED 4 -\endverbatim - -\subsection pdf2dcm_exit_codes_input_file_errors input file errors -\verbatim -EXITCODE_CANNOT_READ_INPUT_FILE 20 -EXITCODE_NO_INPUT_FILES 21 -EXITCODE_INVALID_INPUT_FILE 22 -\endverbatim - -\subsection pdf2dcm_exit_codes_output_file_errors output file errors -\verbatim -EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 -\endverbatim - -\section pdf2dcm_environment ENVIRONMENT - -The \b pdf2dcm utility will attempt to load DICOM data dictionaries specified -in the \e DCMDICTPATH environment variable. By default, i.e. if the -\e DCMDICTPATH environment variable is not set, the file -\/dicom.dic will be loaded unless the dictionary is built -into the application (default for Windows). - -The default behavior should be preferred and the \e DCMDICTPATH environment -variable only used when alternative data dictionaries are required. The -\e DCMDICTPATH environment variable has the same format as the Unix shell -\e PATH variable in that a colon (":") separates entries. On Windows systems, -a semicolon (";") is used as a separator. The data dictionary code will -attempt to load each file specified in the \e DCMDICTPATH environment -variable. -It is an error if no data dictionary can be loaded. +The \b pdf2dcm tool is deprecated. Use \b dcmencap instead, which supports +the same command line parameters, and more. \section pdf2dcm_see_also SEE ALSO -dcm2pdf(1) +dcmencap(1) \section pdf2dcm_copyright COPYRIGHT -Copyright (C) 2005-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2005-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/stl2dcm.man b/dcmdata/docs/stl2dcm.man index 4514b254..70c9a984 100644 --- a/dcmdata/docs/stl2dcm.man +++ b/dcmdata/docs/stl2dcm.man @@ -14,279 +14,15 @@ stl2dcm [options] stlfile-in dcmfile-out \section stl2dcm_description DESCRIPTION -The \b stl2dcm utility reads a STL file (\e stlfile-in), converts it to a -DICOM Encapsulated STL Storage SOP instance and stores the converted data -to an output file (\e dcmfile-out). +The \b stl2dcm tool is deprecated. Use \b dcmencap instead, which supports +the same command line parameters, and more. -\section stl2dcm_parameters PARAMETERS +\section stl2dcm_see_also SEE ALSO -\verbatim -stlfile-in STL input filename to be encapsulated - -dcmfile-out DICOM output filename ("-" for stdout) -\endverbatim - -\section stl2dcm_options OPTIONS - -\subsection stl2dcm_general_options general options -\verbatim - -h --help - print this help text and exit - - --version - print version information and exit - - --arguments - print expanded command line arguments - - -q --quiet - quiet mode, print no warnings and errors - - -v --verbose - verbose mode, print processing details - - -d --debug - debug mode, print debug information - - -ll --log-level [l]evel: string constant - (fatal, error, warn, info, debug, trace) - use level l for the logger - - -lc --log-config [f]ilename: string - use config file f for the logger -\endverbatim - -\subsection stl2dcm_dicom_document_options DICOM document options -\verbatim -document title: - - +t --title [t]itle: string (default: empty) - document title - - +cn --concept-name [CSD] [CV] [CM]: string (default: empty) - coded representation of document title defined by coding - scheme designator CSD, code value CV and code meaning CM - -patient data: - - +pn --patient-name [n]ame: string - patient's name in DICOM PN syntax - - +pi --patient-id [i]d: string - patient identifier - - +pb --patient-birthdate [d]ate: string (YYYYMMDD) - patient's birth date - - +ps --patient-sex [s]ex: string (M, F or O) - patient's sex - -study and series: - - +sg --generate - generate new study and series UIDs (default) - - +st --study-from [f]ilename: string - read patient/study data from DICOM file - - +se --series-from [f]ilename: string - read patient/study/series data from DICOM file - -instance number: - - +i1 --instance-one - use instance number 1 (default, not with +se) - - +ii --instance-inc - increment instance number (only with +se) - - +is --instance-set [i]nstance number: integer - use instance number i - -burned-in annotation: - - +an --annotation-yes - document contains patient identifying data (default) - - -an --annotation-no - document does not contain patient identifying data - -enhanced general equipment: - - +mn --manufacturer [n]ame: string - manufacturer's name - - +mm --manufacturer-model [n]ame: string - manufacturer's model name - - +ds --device-serial [n]umber: string - device serial number - - +sv --software-versions [v]ersions: string - software versions - -3d model measurement units: - - +mu --measurement-units [CSD] [CV] [CM]: string - measurement units with coding scheme designator CSD, - code value CV and code meaning CM (default: UCUM, um, um) -\endverbatim - -\subsection stl2dcm_processing_options processing options -\verbatim -other processing options: - - -k --key [k]ey: gggg,eeee="str", path or dictionary name="str" - add further attribute -\endverbatim - -\subsection stl2dcm_output_options output options -\verbatim -output file format: - - +F --write-file - write file format (default) - - -F --write-dataset - write data set without file meta information - -group length encoding: - - +g= --group-length-recalc - recalculate group lengths if present (default) - - +g --group-length-create - always write with group length elements - - -g --group-length-remove - always write without group length elements - -length encoding in sequences and items: - - +e --length-explicit - write with explicit lengths (default) - - -e --length-undefined - write with undefined lengths - -data set trailing padding (not with --write-dataset): - - -p --padding-off - no padding (implicit if --write-dataset) - - +p --padding-create [f]ile-pad [i]tem-pad: integer - align file on multiple of f bytes - and items on multiple of i bytes -\endverbatim - -\section stl2dcm_notes NOTES - -\subsection stl2dcm_attribute_sources Attribute Sources - -The application may be fed with some additional input for filling mandatory -(and optional) attributes in the new DICOM file like patient, study and series -information: - -- The \e --key option can be used to add further attributes to the DICOM output - file. - -- It is also possible to specify sequences, items and nested attributes using - the \e --key option. In these cases, a special "path" notation has to be - used. Details on this path notation can be found in the documentation of - \b dcmodify. - -- The \e --key option can be present more than once. - -- The value part (after the '=') may be absent causing the attribute to be set - with zero length. - -- Please be advised that the \e --key option is applied at the very end, just - before saving the DICOM file, so there is no value checking whatsoever. - -\section stl2dcm_logging LOGGING - -The level of logging output of the various command line tools and underlying -libraries can be specified by the user. By default, only errors and warnings -are written to the standard error stream. Using option \e --verbose also -informational messages like processing details are reported. Option -\e --debug can be used to get more details on the internal activity, e.g. for -debugging purposes. Other logging levels can be selected using option -\e --log-level. In \e --quiet mode only fatal errors are reported. In such -very severe error events, the application will usually terminate. For more -details on the different logging levels, see documentation of module "oflog". - -In case the logging output should be written to file (optionally with logfile -rotation), to syslog (Unix) or the event log (Windows) option \e --log-config -can be used. This configuration file also allows for directing only certain -messages to a particular output stream and for filtering certain messages -based on the module or application where they are generated. An example -configuration file is provided in \/logger.cfg. - -\section stl2dcm_command_line COMMAND LINE - -All command line tools use the following notation for parameters: square -brackets enclose optional values (0-1), three trailing dots indicate that -multiple values are allowed (1-n), a combination of both means 0 to n values. - -Command line options are distinguished from parameters by a leading '+' or '-' -sign, respectively. Usually, order and position of command line options are -arbitrary (i.e. they can appear anywhere). However, if options are mutually -exclusive the rightmost appearance is used. This behavior conforms to the -standard evaluation rules of common Unix shells. - -In addition, one or more command files can be specified using an '@' sign as a -prefix to the filename (e.g. \@command.txt). Such a command argument -is replaced by the content of the corresponding text file (multiple -whitespaces are treated as a single separator unless they appear between two -quotation marks) prior to any further evaluation. Please note that a command -file cannot contain another command file. This simple but effective approach -allows one to summarize common combinations of options/parameters and avoids -longish and confusing command lines (an example is provided in file -\/dumppat.txt). - -\section stl2dcm_exit_codes EXIT CODES - -The \b stl2dcm utility uses the following exit codes when terminating. This -enables the user to check for the reason why the application terminated. - -\subsection stl2dcm_exit_codes_general general -\verbatim -EXITCODE_NO_ERROR 0 -EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 -EXITCODE_MEMORY_EXHAUSTED 4 -\endverbatim - -\subsection stl2dcm_exit_codes_input_file_errors input file errors -\verbatim -EXITCODE_CANNOT_READ_INPUT_FILE 20 -EXITCODE_NO_INPUT_FILES 21 -EXITCODE_INVALID_INPUT_FILE 22 -\endverbatim - -\subsection stl2dcm_exit_codes_output_file_errors output file errors -\verbatim -EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 -\endverbatim - -\section stl2dcm_environment ENVIRONMENT - -The \b stl2dcm utility will attempt to load DICOM data dictionaries specified -in the \e DCMDICTPATH environment variable. By default, i.e. if the -\e DCMDICTPATH environment variable is not set, the file -\/dicom.dic will be loaded unless the dictionary is built -into the application (default for Windows). - -The default behavior should be preferred and the \e DCMDICTPATH environment -variable only used when alternative data dictionaries are required. The -\e DCMDICTPATH environment variable has the same format as the Unix shell -\e PATH variable in that a colon (":") separates entries. On Windows systems, -a semicolon (";") is used as a separator. The data dictionary code will -attempt to load each file specified in the \e DCMDICTPATH environment -variable. -It is an error if no data dictionary can be loaded. +dcmencap(1) \section stl2dcm_copyright COPYRIGHT -Copyright (C) 2018-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2018-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/docs/xml2dcm.man b/dcmdata/docs/xml2dcm.man index 6957ec80..fee5b990 100644 --- a/dcmdata/docs/xml2dcm.man +++ b/dcmdata/docs/xml2dcm.man @@ -351,6 +351,6 @@ It is an error if no data dictionary can be loaded. \section xml2dcm_copyright COPYRIGHT -Copyright (C) 2003-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2003-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmdata/include/dcmtk/dcmdata/dcbytstr.h b/dcmdata/include/dcmtk/dcmdata/dcbytstr.h index 55712b93..17a62258 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcbytstr.h +++ b/dcmdata/include/dcmtk/dcmdata/dcbytstr.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2019, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -475,7 +475,7 @@ private: /** @name string normalization flags. * These flags can be used with normalizeString() to specify the extent of normalization. */ -//@{ +///@{ /// delete trailing spaces const OFBool DELETE_TRAILING = OFTrue; @@ -484,7 +484,7 @@ const OFBool DELETE_LEADING = OFTrue; /// handle string as multi-valued (components separated by a backslash) const OFBool MULTIPART = OFTrue; -//@} +///@} /* Function to get part out of a String for VM > 1 */ diff --git a/dcmdata/include/dcmtk/dcmdata/dccodec.h b/dcmdata/include/dcmtk/dcmdata/dccodec.h index 55fa5c59..8d5f4d0f 100644 --- a/dcmdata/include/dcmtk/dcmdata/dccodec.h +++ b/dcmdata/include/dcmtk/dcmdata/dccodec.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1997-2023, OFFIS e.V. + * Copyright (C) 1997-2024, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -208,6 +208,19 @@ public: const E_TransferSyntax oldRepType, const E_TransferSyntax newRepType) const = 0; + /** determines the effective value of BitsAllocated that a dataset will have + * after decompression of an image with the given values for bitsAllocated + * and bitsStored. This may differ from the bitsAllocated parameter for example + * if that value is not a multiple of 8. Returns zero if an image with the + * given parameters cannot be decoded with this codec. + * @param bitsAllocated current value of Bits Allocated + * @param bitsStored current value of Bits Stored + * @return value of BitsAllocated after decompression, 0 if no decompression possible + */ + virtual Uint16 decodedBitsAllocated( + Uint16 bitsAllocated, + Uint16 bitsStored) const = 0; + /** determine color model of the decompressed image * @param fromParam representation parameter of current compressed * representation, may be NULL @@ -510,6 +523,21 @@ public: DcmItem *dataset, OFString &decompressedColorModel); + /** determines the effective value of BitsAllocated that a dataset will have + * after decompression of an image with the given values for bitsAllocated + * and bitsStored. This may differ from the bitsAllocated parameter for example + * if that value is not a multiple of 8. Returns zero if an image with the + * given parameters cannot be decoded. + * @param fromType transfer syntax to decode from + * @param bitsAllocated current value of Bits Allocated + * @param bitsStored current value of Bits Stored + * @return value of BitsAllocated after decompression, 0 if no decompression possible + */ + static Uint16 decodedBitsAllocated( + const DcmXfer & fromType, + Uint16 bitsAllocated, + Uint16 bitsStored); + private: /** constructor diff --git a/dcmdata/include/dcmtk/dcmdata/dcdatset.h b/dcmdata/include/dcmtk/dcmdata/dcdatset.h index 22e53851..e85ada73 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcdatset.h +++ b/dcmdata/include/dcmtk/dcmdata/dcdatset.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -380,6 +380,15 @@ class DCMTK_DCMDATA_EXPORT DcmDataset */ virtual OFBool checkForSpecificCharacterSet() const { return OFTrue; } + /** initialize the OriginalXfer and CurrentXfer member variables. + * This method sets the values for OriginalXfer (i.e. the transfer syntax + * in which a dataset is or was originally read) and CurrentXfer + * (i.e. the currently active transfer syntax). + * The method should only be called during import operations. + * @param xfer new value for OriginalXfer and CurrentXfer + */ + void initializeXfer(const E_TransferSyntax xfer); + protected: /** perform checks after reading of the dataset is considered complete. The diff --git a/dcmdata/include/dcmtk/dcmdata/dcddirif.h b/dcmdata/include/dcmtk/dcmdata/dcddirif.h index 4fb52374..a4eb5863 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcddirif.h +++ b/dcmdata/include/dcmtk/dcmdata/dcddirif.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2024, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -804,6 +804,19 @@ class DCMTK_DCMDATA_EXPORT DicomDirInterface const OFString &referencedFileID, const OFFilename &sourceFilename); + /** create or update waveform presentation state record and copy required values + * from dataset + * @param record record to be updated, use NULL to create a new one + * @param fileformat DICOM dataset of the current file + * @param referencedFileID value of the Referenced File ID attribute + * @param sourceFilename name of the source DICOM file + * @return pointer to new or updated record, NULL if an error occurred + */ + DcmDirectoryRecord *buildWfPresentationRecord(DcmDirectoryRecord *record, + DcmFileFormat *fileformat, + const OFString &referencedFileID, + const OFFilename &sourceFilename); + /** create or update waveform record and copy required values from dataset * @param record record to be updated, use NULL to create a new one * @param fileformat DICOM dataset of the current file diff --git a/dcmdata/include/dcmtk/dcmdata/dcdeftag.h b/dcmdata/include/dcmtk/dcmdata/dcdeftag.h index 8f815767..5e5475ed 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcdeftag.h +++ b/dcmdata/include/dcmtk/dcmdata/dcdeftag.h @@ -4,7 +4,7 @@ ** ** User: joergr ** Host: thinkpad2 -** Date: 2024-11-16 10:42:04 +** Date: 2025-11-21 11:54:35 ** Prog: /home/joergr/Source/dcmtk-full/public/dcmdata/libsrc/mkdeftag ** ** From: ../data/dicom.dic @@ -17,12 +17,12 @@ #include "dcmtk/dcmdata/dctagkey.h" -#define DCM_DICT_DEFTAG_BUILD_DATE "2024-11-16 10:42:04" +#define DCM_DICT_DEFTAG_BUILD_DATE "2025-11-21 11:54:35" /* ** Fixed Tags in ascending (gggg,eeee) order. -** Number of entries: 5206 +** Number of entries: 5266 ** Tags with a repeating component (repeating tags) are listed later. */ #define DCM_CommandGroupLength DcmTagKey(0x0000, 0x0000) @@ -317,6 +317,10 @@ #define DCM_FailedStudySequence DcmTagKey(0x0008, 0x119b) #define DCM_StudiesContainingOtherReferencedInstancesSequence DcmTagKey(0x0008, 0x1200) #define DCM_RelatedSeriesSequence DcmTagKey(0x0008, 0x1250) +#define DCM_PrincipalDiagnosisCodeSequence DcmTagKey(0x0008, 0x1301) +#define DCM_PrimaryDiagnosisCodeSequence DcmTagKey(0x0008, 0x1302) +#define DCM_SecondaryDiagnosesCodeSequence DcmTagKey(0x0008, 0x1303) +#define DCM_HistologicalDiagnosesCodeSequence DcmTagKey(0x0008, 0x1304) #define DCM_RETIRED_LossyImageCompressionRetired DcmTagKey(0x0008, 0x2110) #define DCM_DerivationDescription DcmTagKey(0x0008, 0x2111) #define DCM_SourceImageSequence DcmTagKey(0x0008, 0x2112) @@ -380,6 +384,12 @@ #define DCM_RecommendedDisplayFrameRateInFloat DcmTagKey(0x0008, 0x9459) #define DCM_SkipFrameRangeFlag DcmTagKey(0x0008, 0x9460) #define DCM_PatientName DcmTagKey(0x0010, 0x0010) +#define DCM_PersonNamesToUseSequence DcmTagKey(0x0010, 0x0011) +#define DCM_NameToUse DcmTagKey(0x0010, 0x0012) +#define DCM_NameToUseComment DcmTagKey(0x0010, 0x0013) +#define DCM_ThirdPersonPronounsSequence DcmTagKey(0x0010, 0x0014) +#define DCM_PronounCodeSequence DcmTagKey(0x0010, 0x0015) +#define DCM_PronounComment DcmTagKey(0x0010, 0x0016) #define DCM_PatientID DcmTagKey(0x0010, 0x0020) #define DCM_IssuerOfPatientID DcmTagKey(0x0010, 0x0021) #define DCM_TypeOfPatientID DcmTagKey(0x0010, 0x0022) @@ -393,6 +403,13 @@ #define DCM_PatientDeathDateInAlternativeCalendar DcmTagKey(0x0010, 0x0034) #define DCM_PatientAlternativeCalendar DcmTagKey(0x0010, 0x0035) #define DCM_PatientSex DcmTagKey(0x0010, 0x0040) +#define DCM_GenderIdentitySequence DcmTagKey(0x0010, 0x0041) +#define DCM_SexParametersForClinicalUseCategoryComment DcmTagKey(0x0010, 0x0042) +#define DCM_SexParametersForClinicalUseCategorySequence DcmTagKey(0x0010, 0x0043) +#define DCM_GenderIdentityCodeSequence DcmTagKey(0x0010, 0x0044) +#define DCM_GenderIdentityComment DcmTagKey(0x0010, 0x0045) +#define DCM_SexParametersForClinicalUseCategoryCodeSequence DcmTagKey(0x0010, 0x0046) +#define DCM_SexParametersForClinicalUseCategoryReference DcmTagKey(0x0010, 0x0047) #define DCM_PatientInsurancePlanCodeSequence DcmTagKey(0x0010, 0x0050) #define DCM_PatientPrimaryLanguageCodeSequence DcmTagKey(0x0010, 0x0101) #define DCM_PatientPrimaryLanguageModifierCodeSequence DcmTagKey(0x0010, 0x0102) @@ -434,8 +451,9 @@ #define DCM_RegionOfResidence DcmTagKey(0x0010, 0x2152) #define DCM_PatientTelephoneNumbers DcmTagKey(0x0010, 0x2154) #define DCM_PatientTelecomInformation DcmTagKey(0x0010, 0x2155) -#define DCM_EthnicGroup DcmTagKey(0x0010, 0x2160) +#define DCM_RETIRED_EthnicGroup DcmTagKey(0x0010, 0x2160) #define DCM_EthnicGroupCodeSequence DcmTagKey(0x0010, 0x2161) +#define DCM_EthnicGroups DcmTagKey(0x0010, 0x2162) #define DCM_Occupation DcmTagKey(0x0010, 0x2180) #define DCM_SmokingStatus DcmTagKey(0x0010, 0x21a0) #define DCM_AdditionalPatientHistory DcmTagKey(0x0010, 0x21b0) @@ -656,6 +674,18 @@ #define DCM_ImageQualityIndicatorType DcmTagKey(0x0014, 0x40a0) #define DCM_ImageQualityIndicatorMaterial DcmTagKey(0x0014, 0x40a1) #define DCM_ImageQualityIndicatorSize DcmTagKey(0x0014, 0x40a2) +#define DCM_WaveDimensionsDefinitionSequence DcmTagKey(0x0014, 0x4101) +#define DCM_WaveDimensionNumber DcmTagKey(0x0014, 0x4102) +#define DCM_WaveDimensionDescription DcmTagKey(0x0014, 0x4103) +#define DCM_WaveDimensionUnit DcmTagKey(0x0014, 0x4104) +#define DCM_WaveDimensionValueType DcmTagKey(0x0014, 0x4105) +#define DCM_WaveDimensionValuesSequence DcmTagKey(0x0014, 0x4106) +#define DCM_ReferencedWaveDimension DcmTagKey(0x0014, 0x4107) +#define DCM_IntegerNumericValue DcmTagKey(0x0014, 0x4108) +#define DCM_ByteNumericValue DcmTagKey(0x0014, 0x4109) +#define DCM_ShortNumericValue DcmTagKey(0x0014, 0x410a) +#define DCM_SinglePrecisionFloatingPointNumericValue DcmTagKey(0x0014, 0x410b) +#define DCM_DoublePrecisionFloatingPointNumericValue DcmTagKey(0x0014, 0x410c) #define DCM_LINACEnergy DcmTagKey(0x0014, 0x5002) #define DCM_LINACOutput DcmTagKey(0x0014, 0x5004) #define DCM_ActiveAperture DcmTagKey(0x0014, 0x5100) @@ -2775,6 +2805,8 @@ #define DCM_VerificationDateTime DcmTagKey(0x0040, 0xa030) #define DCM_ObservationDateTime DcmTagKey(0x0040, 0xa032) #define DCM_ObservationStartDateTime DcmTagKey(0x0040, 0xa033) +#define DCM_EffectiveStartDateTime DcmTagKey(0x0040, 0xa034) +#define DCM_EffectiveStopDateTime DcmTagKey(0x0040, 0xa035) #define DCM_ValueType DcmTagKey(0x0040, 0xa040) #define DCM_ConceptNameCodeSequence DcmTagKey(0x0040, 0xa043) #define DCM_RETIRED_MeasurementPrecisionDescriptionTrial DcmTagKey(0x0040, 0xa047) @@ -2880,6 +2912,25 @@ #define DCM_CellValuesSequence DcmTagKey(0x0040, 0xa808) #define DCM_RETIRED_UniformResourceLocatorTrial DcmTagKey(0x0040, 0xa992) #define DCM_WaveformAnnotationSequence DcmTagKey(0x0040, 0xb020) +#define DCM_StructuredWaveformAnnotationSequence DcmTagKey(0x0040, 0xb030) +#define DCM_WaveformAnnotationDisplaySelectionSequence DcmTagKey(0x0040, 0xb031) +#define DCM_ReferencedMontageIndex DcmTagKey(0x0040, 0xb032) +#define DCM_WaveformTextualAnnotationSequence DcmTagKey(0x0040, 0xb033) +#define DCM_AnnotationDateTime DcmTagKey(0x0040, 0xb034) +#define DCM_DisplayedWaveformSegmentSequence DcmTagKey(0x0040, 0xb035) +#define DCM_SegmentDefinitionDateTime DcmTagKey(0x0040, 0xb036) +#define DCM_MontageActivationSequence DcmTagKey(0x0040, 0xb037) +#define DCM_MontageActivationTimeOffset DcmTagKey(0x0040, 0xb038) +#define DCM_WaveformMontageSequence DcmTagKey(0x0040, 0xb039) +#define DCM_ReferencedMontageChannelNumber DcmTagKey(0x0040, 0xb03a) +#define DCM_MontageName DcmTagKey(0x0040, 0xb03b) +#define DCM_MontageChannelSequence DcmTagKey(0x0040, 0xb03c) +#define DCM_MontageIndex DcmTagKey(0x0040, 0xb03d) +#define DCM_MontageChannelNumber DcmTagKey(0x0040, 0xb03e) +#define DCM_MontageChannelLabel DcmTagKey(0x0040, 0xb03f) +#define DCM_MontageChannelSourceCodeSequence DcmTagKey(0x0040, 0xb040) +#define DCM_ContributingChannelSourcesSequence DcmTagKey(0x0040, 0xb041) +#define DCM_ChannelWeight DcmTagKey(0x0040, 0xb042) #define DCM_TemplateIdentifier DcmTagKey(0x0040, 0xdb00) #define DCM_RETIRED_TemplateVersion DcmTagKey(0x0040, 0xdb06) #define DCM_RETIRED_TemplateLocalVersion DcmTagKey(0x0040, 0xdb07) @@ -2932,6 +2983,7 @@ #define DCM_ReferencedAssertionUID DcmTagKey(0x0044, 0x0108) #define DCM_ApprovalSubjectSequence DcmTagKey(0x0044, 0x0109) #define DCM_OrganizationalRoleCodeSequence DcmTagKey(0x0044, 0x010a) +#define DCM_RTAssertionsSequence DcmTagKey(0x0044, 0x0110) #define DCM_LensDescription DcmTagKey(0x0046, 0x0012) #define DCM_RightLensSequence DcmTagKey(0x0046, 0x0014) #define DCM_LeftLensSequence DcmTagKey(0x0046, 0x0015) @@ -3058,6 +3110,7 @@ #define DCM_PixelOriginInterpretation DcmTagKey(0x0048, 0x0301) #define DCM_NumberOfOpticalPaths DcmTagKey(0x0048, 0x0302) #define DCM_TotalPixelMatrixFocalPlanes DcmTagKey(0x0048, 0x0303) +#define DCM_TilesOverlap DcmTagKey(0x0048, 0x0304) #define DCM_CalibrationImage DcmTagKey(0x0050, 0x0004) #define DCM_DeviceSequence DcmTagKey(0x0050, 0x0010) #define DCM_ContainerComponentTypeCodeSequence DcmTagKey(0x0050, 0x0012) @@ -4109,6 +4162,11 @@ #define DCM_DVHMinimumDose DcmTagKey(0x3004, 0x0070) #define DCM_DVHMaximumDose DcmTagKey(0x3004, 0x0072) #define DCM_DVHMeanDose DcmTagKey(0x3004, 0x0074) +#define DCM_DoseCalculationModelSequence DcmTagKey(0x3004, 0x0080) +#define DCM_DoseCalculationAlgorithmSequence DcmTagKey(0x3004, 0x0081) +#define DCM_CommissioningStatus DcmTagKey(0x3004, 0x0082) +#define DCM_DoseCalculationModelParameterSequence DcmTagKey(0x3004, 0x0083) +#define DCM_DoseDepositionCalculationMedium DcmTagKey(0x3004, 0x0084) #define DCM_StructureSetLabel DcmTagKey(0x3006, 0x0002) #define DCM_StructureSetName DcmTagKey(0x3006, 0x0004) #define DCM_StructureSetDescription DcmTagKey(0x3006, 0x0006) @@ -4633,6 +4691,8 @@ #define DCM_ScanningSpotSize DcmTagKey(0x300a, 0x0398) #define DCM_ScanSpotSizesDelivered DcmTagKey(0x300a, 0x0399) #define DCM_NumberOfPaintings DcmTagKey(0x300a, 0x039a) +#define DCM_ScanSpotGantryAngles DcmTagKey(0x300a, 0x039b) +#define DCM_ScanSpotPatientSupportAngles DcmTagKey(0x300a, 0x039c) #define DCM_IonToleranceTableSequence DcmTagKey(0x300a, 0x03a0) #define DCM_IonBeamSequence DcmTagKey(0x300a, 0x03a2) #define DCM_IonBeamLimitingDeviceSequence DcmTagKey(0x300a, 0x03a4) diff --git a/dcmdata/include/dcmtk/dcmdata/dcdirrec.h b/dcmdata/include/dcmtk/dcmdata/dcdirrec.h index 7fb0022b..57d6ab89 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcdirrec.h +++ b/dcmdata/include/dcmtk/dcmdata/dcdirrec.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2023, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -132,7 +132,9 @@ typedef enum { /// annotation ERT_Annotation = 49, /// inventory - ERT_Inventory = 50 + ERT_Inventory = 50, + /// wf presentation + ERT_WfPresentation = 51 } E_DirRecType; diff --git a/dcmdata/include/dcmtk/dcmdata/dcdocdec.h b/dcmdata/include/dcmtk/dcmdata/dcdocdec.h new file mode 100644 index 00000000..856a9d66 --- /dev/null +++ b/dcmdata/include/dcmtk/dcmdata/dcdocdec.h @@ -0,0 +1,120 @@ +/* + * + * Copyright (C) 2007-2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmdata + * + * Author: Marco Eichelberg, Tingyan Xu + * + * Purpose: Helper class for extracting encapsulated file from DICOM encapsulated storage object + * + */ + +#ifndef DCDOCDEC_H +#define DCDOCDEC_H + +#include "dcmtk/config/osconfig.h" +#include "dcmtk/dcmdata/dcdefine.h" /* for DCMTK_DCMDATA_EXPORT */ +#include "dcmtk/dcmdata/dcfilefo.h" /* for DcmFileFormat */ + +/** Helper class for extracting the encapsulated document from a + * DICOM encapsulated storage object to file + */ +class DCMTK_DCMDATA_EXPORT DcmDocumentDecapsulator +{ +public: + /// Constructor + DcmDocumentDecapsulator(); + + /// Destructor + virtual ~DcmDocumentDecapsulator(); + + /** set the read mode for reading a DICOM file (default: ERM_autoDetect) + * @param mode new read mode + */ + void setReadMode(E_FileReadMode mode) + { + readMode_ = mode; + } + + /** set the input transfer syntax for reading a DICOM file (default: EXS_Unknown) + * @param xfer new input transfer syntax + */ + void setInputXferSyntax(E_TransferSyntax xfer) + { + inputXfer_ = xfer; + } + + /** set a command line string to be executed after the encapsulated document has been written to file + * @param execString command line string + */ + void setExecString(const char *execString) + { + execString_ = execString; + } + + /** set the filename (path) for the DICOM file to be read + * @param fname input file name + */ + void setInputFile(const char *fname) + { + inputFname_ = fname; + } + + /** set the filename (path) for the output file to be written + * @param fname output file name + */ + void setOutputFile(const char *fname) + { + outputFname_ = fname; + } + + /** load the encapsulated DICOM file into memory. + * @return EC_Normal upon success, an error code otherwise + */ + OFCondition loadDICOMFile(); + + /** extract the encapsulated document, remove a pad byte if necessary, + * and write the document to the output file + * @return EC_Normal upon success, an error code otherwise + */ + OFCondition writeEncapsulatedContentToFile(); + + /** execute the pre-defined command line, replacing the placeholder + * "#f" with the actual output filename. + * @return EC_Normal upon success, an error code otherwise + */ + OFCondition executeCommand(); + +private: + + /// input file read mode + E_FileReadMode readMode_; + + /// input transfer syntax + E_TransferSyntax inputXfer_; + + /// command line string to be executed + const char *execString_; + + /// input filename + const char *inputFname_; + + /// output filename + const char *outputFname_; + + /// DICOM file + DcmFileFormat dicomFile_; + +}; + +#endif // DCDOCDEC_H diff --git a/dcmdata/include/dcmtk/dcmdata/dcelem.h b/dcmdata/include/dcmtk/dcmdata/dcelem.h index 2978e961..ae88f5eb 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcelem.h +++ b/dcmdata/include/dcmtk/dcmdata/dcelem.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -578,6 +578,28 @@ class DCMTK_DCMDATA_EXPORT DcmElement */ virtual OFCondition putFloat32(const Float32 val, const unsigned long pos = 0); + /** insert into the element value a copy of the given Sint64 value. If the + * attribute is multi-valued, all other values remain untouched. + * Requires element to be of corresponding VR, otherwise an error is returned. + * @param val new value to be inserted + * @param pos position for insert operation. Value: pos <= getVM(), i.e. a value + * can be appended to the end of the current element or inserted within the + * existing value field. + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition putSint64(const Sint64 val, const unsigned long pos = 0); + + /** insert into the element value a copy of the given Uint64 value. If the + * attribute is multi-valued, all other values remain untouched. + * Requires element to be of corresponding VR, otherwise an error is returned. + * @param val new value to be inserted + * @param pos position for insert operation. Value: pos <= getVM(), i.e. a value + * can be appended to the end of the current element or inserted within the + * existing value field. + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition putUint64(const Uint64 val, const unsigned long pos = 0); + /** insert into the element value a copy of the given Float64 value. If the * attribute is multi-valued, all other values remain untouched. * Requires element to be of corresponding VR, otherwise an error is returned. @@ -600,7 +622,8 @@ class DCMTK_DCMDATA_EXPORT DcmElement */ virtual OFCondition putTagVal(const DcmTagKey &attrTag, const unsigned long pos = 0); - /** replace the element value by a copy of the given Uint8 array (which is possibly multi-valued). + /** replace the element value by a copy of the given Uint8 array (which is + * possibly multi-valued). * Requires element to be of corresponding VR, otherwise an error is returned. * @param vals new attribute value * @param num number of values in array vals @@ -608,7 +631,8 @@ class DCMTK_DCMDATA_EXPORT DcmElement */ virtual OFCondition putUint8Array(const Uint8 *vals, const unsigned long num); - /** replace the element value by a copy of the given Sint16 array (which is possibly multi-valued). + /** replace the element value by a copy of the given Sint16 array (which is + * possibly multi-valued). * Requires element to be of corresponding VR, otherwise an error is returned. * @param vals new attribute value * @param num number of values in array vals @@ -616,7 +640,8 @@ class DCMTK_DCMDATA_EXPORT DcmElement */ virtual OFCondition putSint16Array(const Sint16 *vals, const unsigned long num); - /** replace the element value by a copy of the given Uint16 array (which is possibly multi-valued). + /** replace the element value by a copy of the given Uint16 array (which is + * possibly multi-valued). * Requires element to be of corresponding VR, otherwise an error is returned. * @param vals new attribute value * @param num number of values in array vals @@ -624,7 +649,8 @@ class DCMTK_DCMDATA_EXPORT DcmElement */ virtual OFCondition putUint16Array(const Uint16 *vals, const unsigned long num); - /** replace the element value by a copy of the given Sint32 array (which is possibly multi-valued). + /** replace the element value by a copy of the given Sint32 array (which is + * possibly multi-valued). * Requires element to be of corresponding VR, otherwise an error is returned. * @param vals new attribute value * @param num number of values in array vals @@ -632,7 +658,8 @@ class DCMTK_DCMDATA_EXPORT DcmElement */ virtual OFCondition putSint32Array(const Sint32 *vals, const unsigned long num); - /** replace the element value by a copy of the given Uint32 array (which is possibly multi-valued). + /** replace the element value by a copy of the given Uint32 array (which is + * possibly multi-valued). * Requires element to be of corresponding VR, otherwise an error is returned. * @param vals new attribute value * @param num number of values in array vals @@ -640,7 +667,8 @@ class DCMTK_DCMDATA_EXPORT DcmElement */ virtual OFCondition putUint32Array(const Uint32 *vals, const unsigned long num); - /** replace the element value by a copy of the given Float32 array (which is possibly multi-valued). + /** replace the element value by a copy of the given Float32 array (which is + * possibly multi-valued). * Requires element to be of corresponding VR, otherwise an error is returned. * @param vals new attribute value * @param num number of values in array vals @@ -648,7 +676,26 @@ class DCMTK_DCMDATA_EXPORT DcmElement */ virtual OFCondition putFloat32Array(const Float32 *vals, const unsigned long num); - /** replace the element value by a copy of the given Float64 array (which is possibly multi-valued). + /** replace the element value by a copy of the given Sint64 array (which is + * possibly multi-valued). + * Requires element to be of corresponding VR, otherwise an error is returned. + * @param vals new attribute value + * @param num number of values in array vals + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition putSint64Array(const Sint64 *vals, const unsigned long num); + + /** replace the element value by a copy of the given Uint64 array (which is + * possibly multi-valued). + * Requires element to be of corresponding VR, otherwise an error is returned. + * @param vals new attribute value + * @param num number of values in array vals + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition putUint64Array(const Uint64 *vals, const unsigned long num); + + /** replace the element value by a copy of the given Float64 array (which is + * possibly multi-valued). * Requires element to be of corresponding VR, otherwise an error is returned. * @param vals new attribute value * @param num number of values in array vals @@ -896,6 +943,19 @@ class DCMTK_DCMDATA_EXPORT DcmElement static OFCondition checkVM(const unsigned long vmNum, const OFString &vmStr); + /** determines the effective value of BitsAllocated that a dataset will have + * after decompression of an image with the given values for bitsAllocated + * and bitsStored. This may differ from the bitsAllocated parameter for example + * if that value is not a multiple of 8. Returns zero if an image with the + * given parameters cannot be decoded. + * @param bitsAllocated current value of Bits Allocated + * @param bitsStored current value of Bits Stored + * @return effective value of BitsAllocated, 0 if no decompression possible + */ + virtual Uint16 decodedBitsAllocated( + Uint16 bitsAllocated, + Uint16 bitsStored) const; + protected: /** This function returns this element's value. The returned value corresponds to the diff --git a/dcmdata/include/dcmtk/dcmdata/dcencdoc.h b/dcmdata/include/dcmtk/dcmdata/dcencdoc.h index 97e6cebe..4c307adc 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcencdoc.h +++ b/dcmdata/include/dcmtk/dcmdata/dcencdoc.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2018-2021, OFFIS e.V. + * Copyright (C) 2018-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -13,7 +13,7 @@ * * Module: dcmdata * - * Author: Pedro Arizpe + * Author: Pedro Arizpe, Marco Eichelberg * * Purpose: Class to control document encapsulation into DICOM files * @@ -22,273 +22,271 @@ #ifndef DCENCDOC_H #define DCENCDOC_H -//make sure OS specific configuration is included first -#include "dcmtk/config/osconfig.h" +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "dcmtk/ofstd/ofstring.h" /* for class OFString */ #include "dcmtk/ofstd/ofcond.h" /* for class OFCondition */ #include "dcmtk/dcmdata/dcdefine.h" /* for DCMTK_DCMDATA_EXPORT */ #include "dcmtk/dcmdata/dcfilefo.h" /* for class DcmFileFormat */ #include "dcmtk/ofstd/ofcmdln.h" /* for OFCmdUnsignedInt */ -struct XMLNode; class OFCommandLine; class OFConsoleApplication; -class OFLogger; +class DcmItem; -/** This class handles common functions of all command line applications - * for document encapsulation. +/** This class handles common functions for the conversion of + * documents into encapsulated DICOM SOP instances. */ class DCMTK_DCMDATA_EXPORT DcmEncapsulatedDocument { public: - ///Constructor - DcmEncapsulatedDocument(); - - ///Destructor - ~DcmEncapsulatedDocument(); - - /** This function is only used to process CDA files. - * It retrieves all entries of an attribute and returns them separated by backslashes. - * @param fileNode the root XML node. - * @param attr the attribute to search for. - * @return OFstring containing all entries found, separated by double backslashes - */ - OFString XMLgetAllAttributeValues(XMLNode fileNode, OFString attr); - - /** This function is only used to process CDA files. - * It retrieves the value from the CDA document - * corresponding to the DCM Tag. According to Standard v. 2013 part20/sect_A.8. - * @param fileNode the root XML node. - * @param attr the tag to search for in the CDA file. - * @return OFstring containing the value of the corresponding tag. - */ - OFString XMLgetAttribute(XMLNode fileNode, DcmTagKey attr); - - /** Retrieves patient, concept and document data from the CDA file and checks for data conflicts - * with series, study and user input. It also retrieves all mediatypes found in the CDA document. - * @param filename The filename of the CDA document. - * @param appLogger The logger of the application calling this method. - * @return EXITCODE_NO_ERROR (0) if successful or error code in case of failure. - */ - int getCDAData(const char *filename, OFLogger &appLogger); - - /** Recursive function used by getAttributeValues to get all occurrences of an attribute as list. - * @param currnode the current XML node to be processed. - * @param results a pointer to the list of strings where the results should be stored. - * @param attr the attribute to search for. - * @return OFTrue if the attribute value was found, OFFalse otherwise. - */ - OFBool XMLsearchAttribute(XMLNode currnode, OFList *results, OFString attr); - - /** Add CDA specific command line options to the OFCommandLine object - * passed to the constructor. - * @param cmd a reference to an OFCommandLine object used to parse - * the command line argument give to the calling application. - * @return none. - */ - void addCDACommandlineOptions(OFCommandLine& cmd); - - /** Add PDF specific command line options to the OFCommandLine object - * passed to the constructor. - * @param cmd a reference to an OFCommandLine object used to parse - * the command line argument give to the calling application. - * @return none. - */ - void addPDFCommandlineOptions(OFCommandLine& cmd); - - /** Add STL specific command line options to the OFCommandLine object - * passed to the constructor. - * @param cmd a reference to an OFCommandLine object used to parse - * the command line argument give to the calling application. - * @return none. - */ - void addSTLCommandlineOptions(OFCommandLine& cmd); - - /** Add general command line options to the OFCommandLine object - * passed to the constructor. - * @param cmd a reference to an OFCommandLine object used to parse - * the command line argument give to the calling application. - * @return none. - */ - void addGeneralOptions(OFCommandLine &cmd); - - /** Add command line options specific for documents to the OFCommandLine - * object passed to the constructor. - * @param cmd a reference to an OFCommandLine object used to parse - * the command line argument give to the calling application. - * @return none. - */ - void addDocumentOptions(OFCommandLine &cmd); - - /** Add command line options specific for output to the OFCommandLine - * object passed to the constructor. - * @param cmd a reference to an OFCommandLine object used to parse - * the command line argument give to the calling application. - * @return none. - */ - void addOutputOptions(OFCommandLine &cmd); - - /** Parse and evaluate the given command line arguments. - * @param app a reference to an OFConsoleApplication object used in the - * calling application. - * @param cmd a reference to an OFCommandLine object used to parse - * the command line argument give to the calling application. - * @return none. - */ - void parseArguments(OFConsoleApplication& app, OFCommandLine& cmd); - - /** Includes basic information into the DICOM file. - * @param dataset a reference to a DcmItem containing the information to be included. - * @param logger The logger of the application calling this method. - * @return EC_Normal if successful, an error code otherwise. - */ - OFCondition createHeader (DcmItem *dataset, - OFLogger& logger); - - /** Correctly inserts encapsulated document data. - * @param dataset The dataset to which we should encapsulate this document. - * @param logger The logger of the application calling this method. - * @return EXITCODE_NO_ERROR (0) if successful or error code in case of failure. - */ - int insertEncapsulatedDocument(DcmItem *dataset, - OFLogger& logger); - - /** Get study or series data from provided file. Generate UID if none present. - * @param appLogger The logger of the application calling this method. - * @return EC_Normal if successful, an error code otherwise. - */ - OFCondition createIdentifiers(OFLogger& appLogger); - - /** Copy override keys over existing keys in given dataset. - * @param outputDset dataset to which the override keys are copied - * @return EC_Normal if successful, an error code otherwise. - */ - OFCondition applyOverrideKeys(DcmDataset *outputDset); - - /** Specifies some attributes that should be inserted after encapsulation - * They will override any identical attributes already existing in the resulting encapsulated - * DICOM object. The override keys are applied at the very end of the conversion and do not - * undergo any validity checking. - * @param ovkeys override keys that can be tags, dictionary names and paths (see DcmPath - * for syntax). Also it is permitted to set a value if appropriate, e. g. "PatientName=Doe^John" - * would be a valid overridekey. - * @return none. - */ - void setOverrideKeys(const OFList& ovkeys); - - /** Returns the input file name. - * @return the input file name as OFString. - */ - OFString getInputFileName(); - - /** Sets the input file name to the given string. - * @param fName the file name to be set. - * @return none. - */ - void setInputFileName(OFString fName); - - /** Returns the output file name. - * @return the output file name as OFString. - */ - OFString getOutputFileName(); - - /** Sets the output file name. - * @param fName the file name to be set. - * @return none. - */ - void setOutputFileName(OFString fName); - - /** Attempt to save the output file . - * @param fileformat the DICOM Fileformat including the output file params. - * @return Error code as condition, if error occurs, EC_Normal otherwise. - */ - OFCondition saveFile(DcmFileFormat fileformat); - - /** Returns the transfer syntax. - * @return the transfer syntax as E_TransferSyntax. - */ - E_TransferSyntax getTransferSyntax(); - - /** Returns the current filetype. - * @return the current filetype as OFString. - */ - OFString getFileType(); - - /** Sets the current filetype. - * @param fType the current filetype. - * @return none. - */ - void setFileType(OFString fType); + + /// document type of encapsulated document + enum DocumentType + { + /// PDF document + DT_pdfDocument, + /// CDA document + DT_cdaDocument, + /// STL document + DT_stlDocument, + /// MTL document + DT_mtlDocument, + /// OBJ document + DT_objDocument, + /// unknown document + DT_unknownDocument, + }; + + ///Constructor + DcmEncapsulatedDocument(); + + ///Destructor + ~DcmEncapsulatedDocument(); + + /** Add CDA specific command line options to the OFCommandLine object + * passed to the constructor. + * @param cmd a reference to an OFCommandLine object used to parse + * the command line argument give to the calling application. + * @return none. + */ + void addCommandlineOptions(OFCommandLine& cmd) const; + + /** Parse and evaluate the given command line arguments. + * @param app a reference to an OFConsoleApplication object used in the + * calling application. + * @param cmd a reference to an OFCommandLine object used to parse + * the command line argument give to the calling application. + * @return none. + */ + void parseArguments(OFConsoleApplication& app, OFCommandLine& cmd); + + /** Identify document format and insert encapsulated document data + * into the dataset + * @return EC_Normal if successful, an error code otherwise. + */ + OFCondition insertEncapsulatedDocument(); + + /** Get study or series data from series file. Generate UID if none present. + * @note This method should be called after insertEncapsulatedDocument() + * to make sure that the document format has been identified. + * @return EC_Normal if successful, an error code otherwise. + */ + OFCondition createIdentifiers(); + + /** Perform format specific processing such as extracting information + * from the document content. + * @note This method should be called after insertEncapsulatedDocument() + * and after createIdentifiers() to make sure that the document format + * has been identified and inconsistencies between DICOM and document + * level can be checked. + * @return EC_Normal if successful, an error code otherwise. + */ + OFCondition formatSpecificProcessing(); + + /** Write header fields to the DICOM dataset + * @note This method should be called after insertEncapsulatedDocument() + * to make sure that the document format has been identified. + * @return EC_Normal if successful, an error code otherwise. + */ + OFCondition createHeader(); + + /** Copy override keys over existing keys in given dataset. + * @return EC_Normal if successful, an error code otherwise. + */ + OFCondition applyOverrideKeys(); + + /** Returns the input file name. + * @return the input file name as OFString. + */ + OFString getInputFileName(); + + /** Returns the output file name. + * @return the output file name as OFString. + */ + OFString getOutputFileName(); + + /** Save the output file. + * @return EC_Normal if successful, an error code otherwise. + */ + OFCondition saveFile(); private: - ///input file name - OFString opt_ifname; - ///output file name - OFString opt_ofname; - - ///optional parameters - ///*patient data - OFString opt_patientBirthdate; - OFString opt_patientID; - OFString opt_patientName; - OFString opt_patientSex; - ///*concept data - OFString opt_conceptCM; - OFString opt_conceptCSD; - OFString opt_conceptCV; - ///*document specific options - OFString opt_documentTitle; - OFString opt_seriesFile; - OFString opt_seriesUID; - OFString opt_studyUID; - - ///*assign default values for file encoding and padding - E_EncodingType opt_oenctype; - E_FileWriteMode opt_writeMode; - E_GrpLenEncoding opt_oglenc; - E_PaddingEncoding opt_opadenc; - E_TransferSyntax opt_oxfer; - OFCmdUnsignedInt opt_filepad; - OFCmdUnsignedInt opt_itempad; - ///*pre-existing series - OFBool opt_readSeriesInfo; - OFBool opt_annotation; - OFBool opt_increment; - - OFCmdSignedInt opt_instance; - /** These attributes are applied to the dataset after conversion - * (They are not checked by the isValid() function). - */ - OFList opt_overrideKeys; - - ///CDA specific variables - OFString cda_mediaTypes; - OFString hl7_InstanceIdentifier; - OFBool opt_override; - - /// STL specific variables - /// Frame of Reference module - OFString opt_frameOfReferenceUID; - OFString opt_positionReferenceIndicator; - ///Enhanced general equipment module - /// Manufacturer (VM 1) - OFString opt_manufacturer; - /// Manufacturer's Model Name (VM 1) - OFString opt_manufacturerModelName; - /// Device Serial Number (VM 1) - OFString opt_deviceSerialNumber; - /// Software Version(s) (VM 1-n) - OFString opt_softwareVersions; - - /// Manufacturing 3D Model Module - /// 3d Model Measurement Units Code Meaning - OFString opt_measurementUnitsCM; - /// 3d Model Measurement Units Code Scheme Designator - OFString opt_measurementUnitsCSD; - /// 3d Model Measurement Units Code Value - OFString opt_measurementUnitsCV; - - ///Type of file currently being converted. - OFString ftype; + + /** Retrieve patient, concept and document data from a CDA file and check for conflicts + * with series, study and user input. Also retrieve all mediatypes found in the CDA document. + * @return EC_Normal if successful, an error code otherwise. + */ + OFCondition getCDAData(); + + /** Add mandatory Frame of Reference Module attributes to dataset. + * @param dataset output dataset + * @return EC_Normal if successful, an error code otherwise. + */ + OFCondition addFrameOfReferenceModule(DcmItem *dataset); + + /** Add mandatory Enhanced General Equipment Module attributes to dataset. + * @param dataset output dataset + * @return EC_Normal if successful, an error code otherwise. + */ + OFCondition addEnhancedGeneralEquipmentModule(DcmItem *dataset); + + /** Add mandatory Manufacturing 3D Model Module attributes to dataset. + * @param dataset output dataset + * @return EC_Normal if successful, an error code otherwise. + */ + OFCondition addManufacturing3DModelModule(DcmItem *dataset); + + ///input file name + OFString ifname_; + + ///output file name + OFString ofname_; + + /// patient's birth date + OFString patientBirthdate_; + + /// patient ID + OFString patientID_; + + /// patient name + OFString patientName_; + + /// patient's sex + OFString patientSex_; + + /// document title: code meaning + OFString conceptCM_; + + /// document title: code scheme designator + OFString conceptCSD_; + + /// document title: code value + OFString conceptCV_; + + /// document title + OFString documentTitle_; + + /// file to read series data from + OFString seriesFile_; + + /// series instance UID + OFString seriesUID_; + + /// study instance UID + OFString studyUID_; + + /// specific character set (from series file) + OFString specificCharSet_; + + /// modality (from series file) + OFString modality_; + + /// DICOM sequence encoding: explicit or undefined length + E_EncodingType oenctype_; + + /// write mode for the DICOM file + E_FileWriteMode writeMode_; + + /// handling of group length encoding + E_GrpLenEncoding oglenc_; + + /// handling of data set trailing padding + E_PaddingEncoding opadenc_; + + /// transfer syntax for the DICOM file + E_TransferSyntax oxfer_; + + /// padding for the main dataset + OFCmdUnsignedInt filepad_; + + /// padding for sequence items + OFCmdUnsignedInt itempad_; + + /// true if we are supposed to read series information from another DICOM file + OFBool readSeriesInfo_; + + /// burned-in annotation present? + OFBool annotation_; + + /// increment instance number from given DICOM series file? + OFBool increment_; + + /// instance number + OFCmdSignedInt instance_; + + /// list of DICOM attributes and attribute values to be applied after conversion + OFList overrideKeys_; + + // CDA specific variables + + /// CDA media types + OFString cda_mediaTypes; + + /// CDA instance identifier + OFString hl7_InstanceIdentifier; + + /// should CDA header values override DICOM values? + OFBool override_; + + // STL/MTL/OBJ specific variables + // Frame of Reference module + + /// frame of reference UID + OFString frameOfReferenceUID_; + + /// position reference indicator + OFString positionReferenceIndicator_; + + // Enhanced general equipment module + + /// manufacturer + OFString manufacturer_; + + /// manufacturer's model name + OFString manufacturerModelName_; + + /// device serial number + OFString deviceSerialNumber_; + + /// software version(s) (VM 1-n) + OFString softwareVersions_; + + // Manufacturing 3D Model Module + + /// 3D model measurement units code meaning + OFString measurementUnitsCM_; + + /// 3D model measurement units code scheme designator + OFString measurementUnitsCSD_; + + /// 3D model measurement units code value + OFString measurementUnitsCV_; + + /// Type of file currently being converted. + DocumentType ftype_; + + /// DICOM file + DcmFileFormat dfile_; }; + #endif // DCENCDOC_H diff --git a/dcmdata/include/dcmtk/dcmdata/dcerror.h b/dcmdata/include/dcmtk/dcmdata/dcerror.h index cca50b20..c8ba944b 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcerror.h +++ b/dcmdata/include/dcmtk/dcmdata/dcerror.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2022, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -80,7 +80,8 @@ const unsigned short OFM_dcmect = 35; * These constants can be used in addition to the general purpose ones * defined in module ofstd. */ -//@{ +///@{ + /// Invalid tag extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_InvalidTag; /// Tag not found @@ -179,16 +180,32 @@ extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_CannotWriteJsonInlineBinar extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_XMLParseError; /// XML validation failure extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_XMLValidationFailure; -/// SOP class mismatch +/// SOP Class mismatch extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_SOPClassMismatch; /// Unknown UID name: No mapping to UID value defined extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_UnknownUIDName; /// Cannot write IS/DS string as JSON number -extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_CannotWriteStringAsJsonNumber; +extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_CannotWriteStringAsJSONNumber; +/// Cannot write bulk data file +extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_CannotWriteBulkDataFile; +/// JSON encoding not supported for encapsulated multi-frame pixel data +extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_CannotWriteJSONMultiframe; +/// Not the expected Type while parsing the JSON file +extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_InvalidJSONType; +/// Invalid JSON content +extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_InvalidJSONContent; +/// BulkDataURI not yet supported +extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_BulkDataURINotSupported; +/// Unsupported URI type +extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_UnsupportedURIType; +/// Execution of command line failed +extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_CommandLineFailed; -//@} +///@} -// status code constants +/** @name status code constants for module dcmdata. + */ +///@{ /// error, cannot select specific character set extern DCMTK_DCMDATA_EXPORT const unsigned short EC_CODE_CannotSelectCharacterSet; @@ -199,4 +216,6 @@ extern DCMTK_DCMDATA_EXPORT const unsigned short EC_CODE_CannotConvertToXML; /// error, cannot determine start fragment (of compressed pixel data) extern DCMTK_DCMDATA_EXPORT const unsigned short EC_CODE_CannotDetermineStartFragment; +///@} + #endif /* !DCERROR_H */ diff --git a/dcmdata/include/dcmtk/dcmdata/dcfilefo.h b/dcmdata/include/dcmtk/dcmdata/dcfilefo.h index ca901e7f..452aa1dd 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcfilefo.h +++ b/dcmdata/include/dcmtk/dcmdata/dcfilefo.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -24,11 +24,9 @@ #define DCFILEFO_H #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ - #include "dcmtk/dcmdata/dcsequen.h" #include "dcmtk/dcmdata/dcdatset.h" - // forward declarations class DcmMetaInfo; class DcmJsonFormat; @@ -390,6 +388,20 @@ class DCMTK_DCMDATA_EXPORT DcmFileFormat FileReadMode = readMode; } + /** set Implementation Class UID to be used when writing the file + * (unless the write mode EWM_dontUpdateMeta is used, + * in which case the metaheader is not updated.) + * @param implementationClassUID implementation class UID + */ + void setImplementationClassUID(const OFString& implementationClassUID); + + /** set Implementation Version Name to be used when writing the file + * (unless the write mode EWM_dontUpdateMeta is used, + * in which case the metaheader is not updated.) + * @param implementationVersionName implementation version name + */ + void setImplementationVersionName(const OFString& implementationVersionName); + /** method inherited from base class that shall not be used for instances of this class. * Method immediately returns with error code. * @param item item @@ -481,12 +493,12 @@ class DCMTK_DCMDATA_EXPORT DcmFileFormat * @param writeMode flag indicating whether to update the file meta information or not * @return EC_Normal if successful, an error code otherwise */ - static OFCondition checkMetaHeaderValue(DcmMetaInfo *metainfo, + OFCondition checkMetaHeaderValue(DcmMetaInfo *metainfo, DcmDataset *dataset, const DcmTagKey &atagkey, DcmObject *obj, const E_TransferSyntax oxfer, - const E_FileWriteMode writeMode); + const E_FileWriteMode writeMode) const; /** read DCM_TransferSyntaxUID from meta header dataset and return as E_TransferSyntax value * @param metainfo meta-header dataset @@ -496,6 +508,12 @@ class DCMTK_DCMDATA_EXPORT DcmFileFormat /// file read mode, specifies whether to read the meta header or not E_FileReadMode FileReadMode; + + /// implementation class UID to write in the meta-header + OFString ImplementationClassUID; + + /// implementation version name to write in the meta-header + OFString ImplementationVersionName; }; diff --git a/dcmdata/include/dcmtk/dcmdata/dcitem.h b/dcmdata/include/dcmtk/dcmdata/dcitem.h index a296c1c9..84d2b6f3 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcitem.h +++ b/dcmdata/include/dcmtk/dcmdata/dcitem.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -353,6 +353,10 @@ class DCMTK_DCMDATA_EXPORT DcmItem * (separated by a backslash) code extension techniques are used and escape sequences * may be encountered in the source string to switch between the specified character * sets. + * @note The conversion code does not perform a thorough validation of the strings to + * be converted. For example, characters that are permitted in the source character + * set but forbidden in DICOM (such as byte positions 0x80-0x9F in ISO_IR 100) may + * be converted without warning or error. * @param fromCharset name of the source character set(s) used for the conversion * @param toCharset name of the destination character set used for the conversion. * Only a single value is permitted (i.e. no code extensions). @@ -375,11 +379,15 @@ class DCMTK_DCMDATA_EXPORT DcmItem * character set can be found in the DICOM standard, e.g. "ISO_IR 100" for ISO 8859-1 * (Latin 1) or "ISO_IR 192" for Unicode in UTF-8. An empty string denotes the * default character repertoire, which is ASCII (7-bit). + * @note The conversion code does not perform a thorough validation of the strings to + * be converted. For example, characters that are permitted in the source character + * set but forbidden in DICOM (such as byte positions 0x80-0x9F in ISO_IR 100) may + * be converted without warning or error. * @param toCharset name of the destination character set used for the conversion. * Only a single value is permitted (i.e. no code extensions). * @param flags optional flag used to customize the conversion (see DCMTypes::CF_xxx) - * @param ignoreCharset if OFTrue, the value of SpecificCharacterSet is ignored. - * Also see checkForSpecificCharacterSet(). + * @param ignoreCharset if OFTrue, the value of SpecificCharacterSet is ignored, + * otherwise the element value is updated. Also see checkForSpecificCharacterSet(). * @return status, EC_Normal if successful, an error code otherwise */ virtual OFCondition convertCharacterSet(const OFString &toCharset, @@ -388,7 +396,11 @@ class DCMTK_DCMDATA_EXPORT DcmItem /** convert all element values that are contained in this item and that are affected * by SpecificCharacterSet from the currently selected source character set to the - * currently selected destination character set + * currently selected destination character set. + * @note The conversion code does not perform a thorough validation of the strings to + * be converted. For example, characters that are permitted in the source character + * set but forbidden in DICOM (such as byte positions 0x80-0x9F in ISO_IR 100) may + * be converted without warning or error. * @param converter character set converter to be used to convert the element values * @return status, EC_Normal if successful, an error code otherwise */ @@ -403,6 +415,17 @@ class DCMTK_DCMDATA_EXPORT DcmItem */ virtual OFCondition convertToUTF8(); + /** update the SpecificCharacterSet (0008,0005) element depending on the given + * parameters. The current value of this element is either replaced or a new + * element is inserted or the existing element is deleted. + * @note This method is called by convertCharacterSet() and convertToUTF8() when + * needed, so there is usually no reason to call it directly. + * @param status error status of previous operations (might also be updated) + * @param converter character set converter used to convert element values + */ + virtual void updateSpecificCharacterSet(OFCondition &status, + const DcmSpecificCharacterSet &converter); + /** insert a new element into the list of elements maintained by this item. * The list of elements is always kept in ascending tag order. * @param elem element to be inserted, must not be contained in this or @@ -599,8 +622,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem /* --- findAndGet functions: find an element and get it or the value, respectively --- */ /** find element and get a pointer to it (or copy it). - * Applicable to all DICOM value representations (VR). * The result variable 'element' is automatically set to NULL if an error occurs. + * @note Applicable to all DICOM value representations (VR). * @param tagKey DICOM tag specifying the attribute to be searched for * @param element variable in which the reference to (or copy of) the element is stored * @param searchIntoSub flag indicating whether to search into sequences or not @@ -614,6 +637,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem /** find all elements matching a particular tag and return references to them on a stack. * This functions always performs a deep search (i.e. searches into sequence of items). + * @note Applicable to all DICOM value representations (VR). * @param tagKey DICOM tag specifying the attribute to be searched for * @param resultStack stack where references to the elements are stored (added to). * If no element is found, the stack is not modified (e.g. cleared). @@ -623,11 +647,11 @@ class DCMTK_DCMDATA_EXPORT DcmItem DcmStack &resultStack); /** find element and get value as a reference to a C string. NB: The string is not copied! - * Applicable to the following VRs: AE, AS, CS, DA, DS, DT, IS, LO, LT, PN, SH, ST, TM, UC, UI, - * UR, UT. * Since the getString() routine is called internally the resulting string reference represents * the (possibly multi-valued) value as stored in the dataset, i.e. no normalization is performed. * The result variable 'value' is automatically set to NULL if an error occurs. + * @note Applicable to the following VRs: AE, AS, CS, DA, DS, DT, IS, LO, LT, PN, SH, ST, TM, + * UC, UI, UR, UT. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the reference to the element value is stored (might be NULL) * @param searchIntoSub flag indicating whether to search into sequences or not @@ -638,14 +662,14 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as a reference to a C string. NB: The string is not copied! - * Applicable to the following VRs: AE, AS, CS, DA, DS, DT, IS, LO, LT, PN, SH, ST, TM, UC, UI, - * UR, UT. * Since the getString() routine is called internally the resulting string reference represents * the (possibly multi-valued) value as stored in the dataset, i.e. no normalization is performed. * The result variable 'value' is automatically set to NULL and 'length' is set to 0 if an error * occurs. * Please note that since the length is returned separately, the string value can contain more * than one NULL byte. + * @note Applicable to the following VRs: AE, AS, CS, DA, DS, DT, IS, LO, LT, PN, SH, ST, TM, + * UC, UI, UR, UT. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the reference to the element value is stored (might be NULL) * @param length length of the string (number of characters without the trailing NULL byte) @@ -658,8 +682,6 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as a C++ string (only one component). - * Applicable to the following VRs: AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, OB, OD, OF, - * OL, OV, OW, PN, SH, SL, SS, ST, SV, TM, UC, UI, UL, UR, US, UT, UV. * Since the getOFString() routine is called internally the resulting string is normalized, i.e. * leading and/or trailing spaces are removed according to the associated value representation, * or the element value is converted to a character string (for non-string VRs) - see documentation @@ -667,6 +689,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem * In contrast to the above and below function only the specified component (see parameter 'pos') * is returned. The result variable 'value' is automatically set to an empty string if an error * occurs. + * @note Applicable to the following VRs: AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, OB, OD, + * OF, OL, OV, OW, PN, SH, SL, SS, ST, SV, TM, UC, UI, UL, UR, US, UT, UV. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the element value is stored * @param pos index of the value in case of multi-valued elements (0..vm-1) @@ -679,13 +703,13 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as a C++ string (all components). - * Applicable to the following VRs: AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, OB, OD, OF, - * OL, OV, OW, PN, SH, SL, SS, ST, SV, TM, UC, UI, UL, UR, US, UT, UV. * Since the getOFStringArray() routine is called internally the resulting string is normalized, * i.e. leading and/or trailing spaces are removed according to the associated value representation * or the element values are converted to character strings (for non-string VRs) - see documentation * in the corresponding header file. * The result variable 'value' is automatically set to an empty string if an error occurs. + * @note Applicable to the following VRs: AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, OB, OD, + * OF, OL, OV, OW, PN, SH, SL, SS, ST, SV, TM, UC, UI, UL, UR, US, UT, UV. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the element value is stored * @param searchIntoSub flag indicating whether to search into sequences or not @@ -696,8 +720,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as an unsigned 8-bit integer. - * Applicable to the following VRs: OB. * The result variable 'value' is automatically set to zero if an error occurs. + * @note Applicable to the following VRs: OB. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the element value is stored * @param pos index of the value in case of multi-valued elements (0..vm-1) @@ -710,8 +734,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as an array of unsigned 8-bit integers. - * Applicable to the following VRs: OB. * The result variable 'value' is automatically set to NULL if an error occurs. + * @note Applicable to the following VRs: OB. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the reference to the element value is stored * @param count stores number of items in the result array (if not NULL) @@ -724,8 +748,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as an unsigned 16-bit integer. - * Applicable to the following VRs: OW, US. * The result variable 'value' is automatically set to zero if an error occurs. + * @note Applicable to the following VRs: OW, US. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the element value is stored * @param pos index of the value in case of multi-valued elements (0..vm-1) @@ -738,8 +762,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as an array of unsigned 16-bit integers. - * Applicable to the following VRs: AT, OW, US. * The result variable 'value' is automatically set to NULL if an error occurs. + * @note Applicable to the following VRs: AT, OW, US. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the reference to the element value is stored * @param count stores number of items in the result array (if not NULL) @@ -752,8 +776,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as a signed 16-bit integer. - * Applicable to the following VRs: SS. * The result variable 'value' is automatically set to zero if an error occurs. + * @note Applicable to the following VRs: SS. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the element value is stored * @param pos index of the value in case of multi-valued elements (0..vm-1) @@ -766,8 +790,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as an array of signed 16-bit integers. - * Applicable to the following VRs: SS. * The result variable 'value' is automatically set to NULL if an error occurs. + * @note Applicable to the following VRs: SS. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the reference to the element value is stored * @param count stores number of items in the result array (if not NULL) @@ -780,8 +804,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as an unsigned 32-bit integer. - * Applicable to the following VRs: OL, UL. * The result variable 'value' is automatically set to zero if an error occurs. + * @note Applicable to the following VRs: OL, UL. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the element value is stored * @param pos index of the value in case of multi-valued elements (0..vm-1) @@ -794,8 +818,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as an array of unsigned 32-bit integers. - * Applicable to the following VRs: OL, UL. * The result variable 'value' is automatically set to NULL if an error occurs. + * @note Applicable to the following VRs: OL, UL. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the reference to the element value is stored * @param count stores number of items in the result array (if not NULL) @@ -808,8 +832,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as a signed 32-bit integer. - * Applicable to the following VRs: IS, SL. * The result variable 'value' is automatically set to zero if an error occurs. + * @note Applicable to the following VRs: IS, SL. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the element value is stored * @param pos index of the value in case of multi-valued elements (0..vm-1) @@ -822,8 +846,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as an array of signed 32-bit integers. - * Applicable to the following VRs: SL. * The result variable 'value' is automatically set to NULL if an error occurs. + * @note Applicable to the following VRs: SL. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the reference to the element value is stored * @param count stores number of items in the result array (if not NULL) @@ -836,8 +860,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as an unsigned 64-bit integer. - * Applicable to the following VRs: OV, UV. * The result variable 'value' is automatically set to zero if an error occurs. + * @note Applicable to the following VRs: OV, UV. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the element value is stored * @param pos index of the value in case of multi-valued elements (0..vm-1) @@ -850,8 +874,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as an array of unsigned 64-bit integers. - * Applicable to the following VRs: OV, UV. * The result variable 'value' is automatically set to NULL if an error occurs. + * @note Applicable to the following VRs: OV, UV. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the reference to the element value is stored * @param count stores number of items in the result array (if not NULL) @@ -864,8 +888,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as a signed 64-bit integer. - * Applicable to the following VRs: SV. * The result variable 'value' is automatically set to zero if an error occurs. + * @note Applicable to the following VRs: SV. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the element value is stored * @param pos index of the value in case of multi-valued elements (0..vm-1) @@ -878,8 +902,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as an array of signed 64-bit integers. - * Applicable to the following VRs: SV. * The result variable 'value' is automatically set to NULL if an error occurs. + * @note Applicable to the following VRs: SV. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the reference to the element value is stored * @param count stores number of items in the result array (if not NULL) @@ -892,8 +916,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as a (signed) long integer. - * Applicable to the following VRs: IS, OL, SL, SS, UL, US. * The result variable 'value' is automatically set to zero if an error occurs. + * @note Applicable to the following VRs: IS, OL, SL, SS, UL, US. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the element value is stored * @param pos index of the value in case of multi-valued elements (0..vm-1) @@ -906,8 +930,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as a 32-bit floating point. - * Applicable to the following VRs: FL, OF. * The result variable 'value' is automatically set to zero if an error occurs. + * @note Applicable to the following VRs: FL, OF. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the element value is stored * @param pos index of the value in case of multi-valued elements (0..vm-1) @@ -920,8 +944,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as an array of 32-bit floating point values. - * Applicable to the following VRs: FL, OF. * The result variable 'value' is automatically set to NULL if an error occurs. + * @note Applicable to the following VRs: FL, OF. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the reference to the element value is stored * @param count stores number of items in the result array (if not NULL) @@ -934,8 +958,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as a 64-bit floating point. - * Applicable to the following VRs: DS, FD, OD. * The result variable 'value' is automatically set to zero if an error occurs. + * @note Applicable to the following VRs: DS, FD, OD. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the element value is stored * @param pos index of the value in case of multi-valued elements (0..vm-1) @@ -948,8 +972,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** find element and get value as an array of 64-bit floating point values. - * Applicable to the following VRs: FD, OD. * The result variable 'value' is automatically set to NULL if an error occurs. + * @note Applicable to the following VRs: FD, OD. * @param tagKey DICOM tag specifying the attribute to be searched for * @param value variable in which the reference to the element value is stored * @param count stores number of items in the result array (if not NULL) @@ -962,9 +986,9 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** looks up and returns a given sequence. - * Applicable to the following VRs: SQ, (pixelSQ). * The result variable 'sequence' is automatically set to NULL if an error occurs * (e.g. if 'seqTagKey' does not refer to a sequence attribute). + * @note Applicable to the following VRs: SQ, (pixelSQ). * @param seqTagKey DICOM tag specifying the sequence attribute to be searched for * @param sequence variable in which the reference to (or copy of) the sequence is stored * @param searchIntoSub flag indicating whether to search into sub-sequences or not @@ -980,8 +1004,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem * to NULL and returns EC_TagNotFound (specified sequence does not exist) or * EC_IllegalParameter (specified item does not exist). Only the top-most level of * the dataset/item is examined (i.e. no deep-search is performed). - * Applicable to the following VRs: SQ, (pixelSQ). * Please note that an instance of the DcmPixelItem class cannot be retrieved. + * @note Applicable to the following VRs: SQ, (pixelSQ). * @param seqTagKey DICOM tag specifying the sequence attribute to be searched for * @param item variable in which the reference to (or copy of) the item is stored * @param itemNum number of the item to be searched for (0..n-1, -1 for last) @@ -1000,9 +1024,9 @@ class DCMTK_DCMDATA_EXPORT DcmItem * If either the sequence or the item do not exist, they are created. If necessary, * multiple empty items are inserted. Only the top-most level of the dataset/item * is examined (i.e. no deep-search is performed). - * Applicable to the following VRs: SQ, (pixelSQ). * Please note that an instance of the DcmPixelItem class cannot be retrieved or * created. + * @note Applicable to the following VRs: SQ, (pixelSQ). * @param seqTag DICOM tag specifying the sequence attribute to be searched for * (or to be created) * @param item variable in which the reference to the sequence item is stored @@ -1021,7 +1045,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem * of items). Empty elements are also copied. However, if the given tag is not * found in the current dataset, EC_TagNotFound is returned and the destination * dataset remains unchanged. - * Applicable to all DICOM value representations (VR). + * @note Applicable to all DICOM value representations (VR). * @param tagKey DICOM tag specifying the attribute to be searched for * @param destItem destination dataset to which the copied element is inserted * @param replaceOld flag indicating whether to replace an existing element or not @@ -1032,7 +1056,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** find element, remove it from the dataset and free the associated memory. - * Applicable to all DICOM value representations (VR). + * @note Applicable to all DICOM value representations (VR). * @param tagKey DICOM tag specifying the attribute to be searched for * @param allOccurrences flag indicating whether to delete all occurrences of the * attribute tag or the first one only (implies 'searchIntoSub' to be true) @@ -1044,7 +1068,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool searchIntoSub = OFFalse); /** looks up the given sequence in the current dataset and deletes the given item. - * Applicable to the following VRs: SQ, (pixelSQ). + * @note Applicable to the following VRs: SQ, (pixelSQ). * @param seqTagKey DICOM tag specifying the sequence attribute to be searched for * @param itemNum number of the item to be deleted (0..n-1, -1 for last) * @return EC_Normal upon success, an error otherwise. @@ -1056,8 +1080,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem /* --- putAndInsert functions: put value and insert new element --- */ /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, OB, OD, OF, - * OL, OV, OW, PN, SH, SL, SS, ST, SV, TM, UC, UI, UL, UR, US, UT, UV. + * @note Applicable to the following VRs: AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, OB, + * OD, OF, OL, OV, OW, PN, SH, SL, SS, ST, SV, TM, UC, UI, UL, UR, US, UT, UV. * @param tag DICOM tag specifying the attribute to be created * @param value string value to be set for the new element (might be empty or NULL). The format * of the string value is specified by the putString() method of the corresponding VR class. @@ -1069,10 +1093,10 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, OB, OD, OF, - * OL, OV, OW, PN, SH, SL, SS, ST, SV, TM, UC, UI, UL, UR, US, UT, UV. * Please note that since the length of the string has to be specified explicitly, the string * can contain more than one NULL byte. + * @note Applicable to the following VRs: AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, OB, + * OD, OF, OL, OV, OW, PN, SH, SL, SS, ST, SV, TM, UC, UI, UL, UR, US, UT, UV. * @param tag DICOM tag specifying the attribute to be created * @param value string value to be set for the new element (might be empty or NULL). The format * of the string value is specified by the putString() method of the corresponding VR class. @@ -1086,8 +1110,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, OB, OD, OF, - * OL, OV, OW, PN, SH, SL, SS, ST, SV, TM, UC, UI, UL, UR, US, UT, UV. + * @note Applicable to the following VRs: AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, OB, + * OD, OF, OL, OV, OW, PN, SH, SL, SS, ST, SV, TM, UC, UI, UL, UR, US, UT, UV. * @param tag DICOM tag specifying the attribute to be created * @param value string value to be set for the new element (might be empty). The format of the * string value is specified by the putOFStringArray() method of the corresponding VR class. @@ -1099,7 +1123,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: OB, ox (polymorph OB/OW or pixel data). + * @note Applicable to the following VRs: OB, ox (polymorph OB/OW), px (pixel data). * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element (might be NULL) * @param count number of values (= bytes in this case) to be copied from 'value' @@ -1112,7 +1136,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: US, xs (US or SS). + * @note Applicable to the following VRs: US, xs (US or SS). * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element * @param pos index of the value to be set (0..vm). A value can be appended to @@ -1126,7 +1150,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: AT, OW, US, ox (polymorph OB/OW or pixel data), xs (US or SS). + * @note Applicable to the following VRs: AT, OW, US, ox (polymorph OB/OW), px (pixel data), + * xs (US or SS). * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element (might be NULL) * @param count number of values (not bytes!) to be copied from 'value' @@ -1139,7 +1164,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: SS, xs (US or SS). + * @note Applicable to the following VRs: SS, xs (US or SS). * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element * @param pos index of the value to be set (0..vm). A value can be appended to @@ -1153,7 +1178,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: SS, xs (US or SS). + * @note Applicable to the following VRs: SS, xs (US or SS). * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element * @param count number of values (not bytes!) to be copied from 'value' @@ -1166,7 +1191,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: OL, UL. + * @note Applicable to the following VRs: OL, UL. * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element * @param pos index of the value to be set (0..vm). A value can be appended to @@ -1180,7 +1205,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: OL, UL. + * @note Applicable to the following VRs: OL, UL. * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element * @param count number of values (not bytes!) to be copied from 'value' @@ -1193,7 +1218,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: SL. + * @note Applicable to the following VRs: SL. * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element * @param pos index of the value to be set (0..vm). A value can be appended to @@ -1207,7 +1232,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: SL. + * @note Applicable to the following VRs: SL. * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element * @param count number of values (not bytes!) to be copied from 'value' @@ -1220,7 +1245,61 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: FL, OF. + * @note Applicable to the following VRs: OV, UV. + * @param tag DICOM tag specifying the attribute to be created + * @param value value to be set for the new element + * @param pos index of the value to be set (0..vm). A value can be appended to + * the end of or inserted within the existing value field. + * @param replaceOld flag indicating whether to replace an existing element or not + * @return EC_Normal upon success, an error code otherwise. + */ + OFCondition putAndInsertUint64(const DcmTag &tag, + const Uint64 value, + const unsigned long pos = 0, + const OFBool replaceOld = OFTrue); + + /** create a new element, put specified value to it and insert the element into the dataset/item. + * @note Applicable to the following VRs: OV, UV. + * @param tag DICOM tag specifying the attribute to be created + * @param value value to be set for the new element + * @param count number of values (not bytes!) to be copied from 'value' + * @param replaceOld flag indicating whether to replace an existing element or not + * @return EC_Normal upon success, an error code otherwise. + */ + OFCondition putAndInsertUint64Array(const DcmTag &tag, + const Uint64 *value, + const unsigned long count, + const OFBool replaceOld = OFTrue); + + /** create a new element, put specified value to it and insert the element into the dataset/item. + * @note Applicable to the following VRs: SV. + * @param tag DICOM tag specifying the attribute to be created + * @param value value to be set for the new element + * @param pos index of the value to be set (0..vm). A value can be appended to + * the end of or inserted within the existing value field. + * @param replaceOld flag indicating whether to replace an existing element or not + * @return EC_Normal upon success, an error code otherwise. + */ + OFCondition putAndInsertSint64(const DcmTag &tag, + const Sint64 value, + const unsigned long pos = 0, + const OFBool replaceOld = OFTrue); + + /** create a new element, put specified value to it and insert the element into the dataset/item. + * @note Applicable to the following VRs: SV. + * @param tag DICOM tag specifying the attribute to be created + * @param value value to be set for the new element + * @param count number of values (not bytes!) to be copied from 'value' + * @param replaceOld flag indicating whether to replace an existing element or not + * @return EC_Normal upon success, an error code otherwise. + */ + OFCondition putAndInsertSint64Array(const DcmTag &tag, + const Sint64 *value, + const unsigned long count, + const OFBool replaceOld = OFTrue); + + /** create a new element, put specified value to it and insert the element into the dataset/item. + * @note Applicable to the following VRs: FL, OF. * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element * @param pos index of the value to be set (0..vm). A value can be appended to @@ -1234,7 +1313,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: FL, OF. + * @note Applicable to the following VRs: FL, OF. * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element * @param count number of values (not bytes!) to be copied from 'value' @@ -1247,7 +1326,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: DS, FD, OD + * @note Applicable to the following VRs: DS, FD, OD * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element * @param pos index of the value to be set (0..vm). A value can be appended to @@ -1261,7 +1340,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: FD, OD. + * @note Applicable to the following VRs: FD, OD. * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element * @param count number of values (not bytes!) to be copied from 'value' @@ -1274,7 +1353,7 @@ class DCMTK_DCMDATA_EXPORT DcmItem const OFBool replaceOld = OFTrue); /** create a new element, put specified value to it and insert the element into the dataset/item. - * Applicable to the following VRs: AT. + * @note Applicable to the following VRs: AT. * @param tag DICOM tag specifying the attribute to be created * @param value value to be set for the new element * @param pos index of the value to be set (0..vm). A value can be appended to @@ -1290,8 +1369,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem /* --- insertXXX functions: insert new element --- */ /** create a new element (with no value) and insert it into the dataset/item. - * Applicable to the following VRs: AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, OB, OD, OF, - * OL, OV, OW, PN, SH, SL, SQ, SS, ST, SV, TM, UC, UI, UL, UR, US, UT, UV. + * @note Applicable to the following VRs: AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, + * OB, OD, OF, OL, OV, OW, PN, SH, SL, SQ, SS, ST, SV, TM, UC, UI, UL, UR, US, UT, UV. * @param tag DICOM tag specifying the attribute to be created * @param replaceOld flag indicating whether to replace an existing element or not * @return EC_Normal upon success, an error code otherwise. @@ -1303,8 +1382,8 @@ class DCMTK_DCMDATA_EXPORT DcmItem * If the sequence does not exist, it is created. If necessary, multiple empty items * are inserted before the specified item position. Only the top-most level of the * dataset/item is examined (i.e. no deep-search is performed). - * Applicable to the following VRs: SQ, (pixelSQ). * Please note that an instance of the DcmPixelItem class cannot be inserted. + * @note Applicable to the following VRs: SQ, (pixelSQ). * @param seqTag DICOM tag specifying the sequence attribute to be searched for * (or to be created) * @param item item to be inserted into the sequence, must not be contained in this @@ -1443,15 +1522,6 @@ class DCMTK_DCMDATA_EXPORT DcmItem OFBool checkAndUpdateVR(DcmItem &item, DcmTag &tag); - /** update the SpecificCharacterSet (0008,0005) element depending on the given - * parameters. The current value of this element is either replaced or a new - * element is inserted or the existing element is deleted. - * @param status error status of previous operations (might also be updated) - * @param converter character set converter used to convert element values - */ - void updateSpecificCharacterSet(OFCondition &status, - const DcmSpecificCharacterSet &converter); - /** creates new DICOM element from given attribute tag. * Helper function used by DICOM parser (friend of this class) and thus * hidden from the public interface. DcmItem's readSubElement() uses diff --git a/dcmdata/include/dcmtk/dcmdata/dcjson.h b/dcmdata/include/dcmtk/dcmdata/dcjson.h index 47818159..891443c2 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcjson.h +++ b/dcmdata/include/dcmtk/dcmdata/dcjson.h @@ -1,6 +1,6 @@ /* * -* Copyright (C) 2017-2022, OFFIS e.V. +* Copyright (C) 2017-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -23,12 +23,12 @@ #define DCJSON_H #include "dcmtk/config/osconfig.h" // make sure OS specific configuration is included first - #include "dcmtk/ofstd/ofdefine.h" #include "dcmtk/ofstd/ofstring.h" - #include "dcmtk/dcmdata/dctagkey.h" +class OFCondition; + /** Class for handling JSON format options. * Base class to implement custom formatting. * Purpose: @@ -88,9 +88,9 @@ public: }; /** A class to create small proxy objects that ease indention handling. - * Each Indention object only contains a reference to the DcmJsonFormat object - * that created it and its only purpose is to call the respective methods - * of that object when one of its overloaded operators is used. + * Each Indention object only contains a reference to the DcmJsonFormat + * object that created it and its only purpose is to call the respective + * methods of that object when one of its overloaded operators is used. */ class Indention { @@ -214,14 +214,17 @@ public: OFString &value); /** Constructor - * @param printMetaInfo parameter that defines if meta information should be written + * @param printMetaInfo parameter that defines if meta information should + * be written */ inline DcmJsonFormat(const OFBool printMetaInfo) : printMetaheaderInformation(printMetaInfo) , enableJsonExtension(OFFalse) , numStringPolicy(NSP_auto) + , minBulkDataSize(-1) + , bulkDataURIPrefix() + , bulkDataDirectory() { - } /** Virtual destructor, does nothing @@ -238,7 +241,8 @@ public: */ virtual OFString space() = 0; - /** Method to return an indention proxy object for increasing, decreasing or printing indention + /** Method to return an indention proxy object for increasing, decreasing or + * printing indention * @return an indention proxy object. */ inline Indention indent() @@ -250,37 +254,20 @@ public: * Override this function to implement bulk data URI output. * @param tag the tag of the attribute being printed, for letting * the implementation decide how to handle it. - * @param uri the resulting URI to output. + * @param len the length of the attribute value, in bytes * @return OFTrue if yes, OFFalse if no. - * @details - *

Usage Example:

- * @code{.cpp} - * struct BulkDataURIJsonFormat : DcmJsonFormatPretty - * { - * CustomJsonFormat(const OFBool printMetaInfo = OFTrue, - * ... bulkDataURIDatabase) - * : DcmJsonFormatPretty(printMetaInfo) - * , TheDatabase(bulkDataURIDatabase) - * { - * - * } - * - * virtual OFBool asBulkDataURI(const DcmTagKey& tag, OFString& uri) - * { - * ... result = TheDatabase.findBulkDataFor(tag); - * if (result.found()) - * { - * uri = result.uri(); - * return OFTrue; - * } - * return OFFalse; - * } - * - * ... TheDatabase; - * } - * @endcode */ - virtual OFBool asBulkDataURI(const DcmTagKey& tag, OFString& uri); + virtual OFBool asBulkDataURI(const DcmTagKey& tag, Uint32 len) const; + + /** return path of bulk data directory + * @param directory path returned in this parameter + */ + virtual void getBulkDataDirectory(OFString& directory) const; + + /** return the current bulk data URI prefix + * @return prefix current bulk data URI prefix returned in this parameter + */ + virtual void getBulkDataURIPrefix(OFString& prefix) const; /** Print the Prefix which for JSON Values needed * with indention and newlines as in the format Variable given. @@ -367,6 +354,51 @@ public: */ const OFBool printMetaheaderInformation; + /** set the minimum size of binary attributes stored as bulk data. + * @param min_bulk_size minimum bulk data size in kBytes, negative number for no bulk data + */ + virtual void setMinBulkSize(ssize_t min_bulk_size); + + /** set the prefix for URIs generated for bulk data + * @param bulk_uri_prefix URI prefix string + */ + virtual void setBulkURIPrefix(const char *bulk_uri_prefix); + + /** set the directory to which bulk data files should be written + * @param bulk_dir directory for bulk data files, must exist and be writable + */ + virtual void setBulkDir(const char *bulk_dir); + + /** write an attribute as BulkDataURI. + * @param out output stream + * @param tagkey tag key of the attribute + * @param len length of the attribute value + * @param byteValues pointer to the raw attribute value in little endian byte order + * @param extension file name extension + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition writeBulkData( + STD_NAMESPACE ostream &out, + const DcmTagKey& tagkey, + Uint32 len, + Uint8 *byteValues, + const char *extension = ".bin"); + + /** write a binary attribute either as InlineBinary or as BulkDataURI. + * @param out output stream + * @param tagkey tag key of the attribute + * @param len length of the attribute value + * @param byteValues pointer to the raw attribute value in little endian byte order + * @param extension file name extension + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition writeBinaryAttribute( + STD_NAMESPACE ostream &out, + const DcmTagKey& tagkey, + Uint32 len, + Uint8 *byteValues, + const char *extension = ".bin"); + protected: /** Indent to the specific level. * @param out output stream to which the indention is written. @@ -394,6 +426,22 @@ private: * as number or string. Default is NSP_auto. */ NumStringPolicy numStringPolicy; + + /** minimum size of binary attributes to be written as bulk data, + * in kBytes. A negative value means that no bulk data is written. + */ + ssize_t minBulkDataSize; + + /** prefix for bulk data URIs to be generated. The filename will + * be appended to this prefix. + */ + OFString bulkDataURIPrefix; + + /** directory to which files for bulk data will be written. + * Must exist and be writable. + */ + OFString bulkDataDirectory; + }; diff --git a/dcmdata/include/dcmtk/dcmdata/dcjsonrd.h b/dcmdata/include/dcmtk/dcmdata/dcjsonrd.h new file mode 100644 index 00000000..85c125a1 --- /dev/null +++ b/dcmdata/include/dcmtk/dcmdata/dcjsonrd.h @@ -0,0 +1,345 @@ +/* + * + * Copyright (C) 2024-2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmdata + * + * Author: Tingyan Xu, Marco Eichelberg + * + * Purpose: Class for converting JSON DICOM documents to binary DICOM files + * + */ + +#ifndef DCJSONRD_H +#define DCJSONRD_H + +class OFCondition; + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/ofstd/oftypes.h" /* for OFBool */ +#include "dcmtk/dcmdata/dcdefine.h" /* for DCMTK_DCMDATA_EXPORT */ +#include "dcmtk/dcmdata/dcxfer.h" /* for E_TransferSyntax */ +#include "dcmtk/ofstd/oflist.h" /* for OFList */ + +#define JSMN_HEADER +#include "dcmtk/ofstd/ofjsmn.h" /* for JSMN declarations */ + +typedef jsmn_parser OFJsmnParser; +typedef jsmntok_t OFJsmnToken; +typedef OFJsmnToken *OFJsmnTokenPtr; + +class DcmFileFormat; +class DcmElement; +class DcmTagKey; +class DcmTag; +class DcmSequenceOfItems; +class DcmItem; + + +/** input stream that reads from standard input + */ +class DCMTK_DCMDATA_EXPORT DcmJSONReader +{ +public: + /// constructor + DcmJSONReader(); + + /// destructor + virtual ~DcmJSONReader(); + + /// delete all internal buffers, but keep policy settings + virtual void clear(); + + /** read a JSON file and store the content in this object + * @param ifname name of the JSON file to be read + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition readJSONFile(const char *ifname); + + /** read a JSON dataset from stdin and store the content in this object + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition readJSONFromStdin(); + + /** set the "ignore bulk data URI" policy + * @param value new policy, true = ignore bulk data URIs + */ + virtual void setIgnoreBulkdataURIPolicy(OFBool value) { ignoreBulkdataURIPolicy_ = value; } + + /** set the "stop on error" policy + * @param value new policy, true = stop when encountering a parse error + */ + virtual void setStopOnErrorPolicy(OFBool value) { stopOnErrorPolicy_ = value; } + + /** set the "ignore meta info" policy + * @param value new policy, true = ignore meta info elements in JSON dataset + */ + virtual void setIgnoreMetaInfoPolicy(OFBool value) { ignoreMetaInfoPolicy_ = value; } + + /** set the array handling policy + * @param value new policy + * - -1: reject arrays with more than one dataset + * - 0: store arrays with multiple datasets as a private sequence + * - n > 0: select dataset n from the array, ignore all others */ + virtual void setArrayHandlingPolicy(signed long value) { arrayHandlingPolicy_ = value; } + + /** set the transfer syntax for the dataset + * @param value transfer syntax + */ + virtual void setTransferSyntax(E_TransferSyntax value) { xferSyntax_ = value; } + + /** set the transfer syntax for the dataset + * @param value transfer syntax + */ + virtual E_TransferSyntax getTransferSyntax() const { return xferSyntax_; } + + /** parse the JSON file with the given filename + * @param fileformat DcmFileFormat instance to be populated with the parsed JSON content + * @param ifname name of the JSON file to be read + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition readAndConvertJSONFile( + DcmFileFormat& fileformat, + const char *ifname); + + /** dump the token array produced by the JSMN parser to stderr + */ + virtual OFCondition dumpJSONTokenArray(); + + /** check if the given URI is a file URI + * @param uri URI to check + * @return true if the URI is a file URI + */ + virtual OFBool isFileURI(const OFString& uri) const; + + /** check if the given URI is a http: or https: URI + * @param uri URI to check + * @return true if the URI is a http: or https: URI + */ + virtual OFBool isHttpURI(const OFString& uri) const; + + /** URL decode the given URI, i.e. replace all instances of '%xx' with the hex number xx + * by the corresponding byte. + * @param uri URI string, will be modified by this method. + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition urlDecode(OFString& uri) const; + + /** convert a file URI to a file path in the local filesystem + * @param uri the file URI to convert + * @param filepath the file path is returned in this parameter + * @param offset the byte offset within the file is returned in this parameter + * @param length the number of bytes to read from the file is returned in this parameter, 0 means 'unlimited' + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition fileURItoPath(const OFString& uri, OFString& filepath, size_t& offset, size_t& length) const; + + /** normalize a file path into an absolute path without symbolic links. + * On Windows, the long version of directory and file names will be generated + * and converted to uppercase, since the Win32 file API uses case insensitive filenames + * @param filepath_in the file path to be normalized + * @param filepath_out the normalized file path is returned in this parameter. + * This parameter must not reference the same string as filepath_in. + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition normalizePath(const OFString& filepath_in, OFString& filepath_out) const; + + /** add a path from which bulk data files may be read if referenced by a + * file BulkdataURI. The path will be normalized before being stored. + * All subdirectories of the given directory will also be accepted a valid paths. + * @param dirpath the directory path that is acceptable for bulk data + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition addPermittedBulkdataPath(const OFString& dirpath); + + /** check if the given normalized path (which may include a filename) + * is in the list of permitted bulk data paths as defined by calls to + * addPermittedBulkdataPath(). + * @param filepath the file path to be checked + * @return OFTrue if path is permitted, OFFalse otherwise + */ + virtual OFBool bulkdataPathPermitted(const OFString& filepath) const; + + /** load bulk data from file and insert it into the given element + * @param element element into which the value will be inserted + * @param filepath path to the bulk data file + * @param offset offset in bytes within the file from where to start reading + * @param length number of bytes to read from file, 0 for the entire file from the given offset + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition loadBulkdataFile( + DcmElement& element, + const OFString& filepath, + size_t offset, + size_t length); + +private: + + /// null terminated character string containing the entire JSON dataset + char *jsonDataset_; + + /// size of jsonDataset_ in bytes, not including the terminating null byte + size_t jsonDatasetLen_; + + /// array of parsed JSON tokens + OFJsmnTokenPtr tokenArray_; + + /// number of tokens in tokenArray_ + int tokenNumber_; + + /// policy for handling bulk data URIs (true = ignore) + OFBool ignoreBulkdataURIPolicy_; + + /// policy for error handling (false = ignore) + OFBool stopOnErrorPolicy_; + + /// policy for handling meta info elements in the JSON dataset (true = ignore) + OFBool ignoreMetaInfoPolicy_; + + /** policy for handling arrays of multiple datasets. + * - -1: reject arrays with more than one dataset + * - 0: store arrays with multiple datasets as a private sequence + * - n > 0: select dataset n from the array, ignore all others + */ + signed long arrayHandlingPolicy_; + + /// transfer syntax of the dataset, default: LittleEndianExplicit + E_TransferSyntax xferSyntax_; + + /// list of directories to which file URIs may point + OFList permittedBulkdataDirs_; + + /** calculate the required number of tokens for the JSON dataset + * and allocate the token array accordingly + */ + OFCondition reserveTokens(); + + /** parse the JSON dataset that has be read into the buffer + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition parseJSON(); + + /// private unimplemented copy constructor + DcmJSONReader(const DcmJSONReader&); + + /// private unimplemented copy assignment operator + DcmJSONReader& operator=(const DcmJSONReader&); + + /** helper function to retrieve the content of the token. + * There is no check on boundaries! + * @param value the result string + * @param t token pointer + */ + void getTokenContent( + OFString& value, + OFJsmnTokenPtr t); + + /** create DICOM element using the given tag and VR + * @param newElem pointer to newly created DICOM element returned in this parameter + * @param dcmTag attribute tag + * @param vr the string representation of the VR (if present) + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition createElement( + DcmElement *& newElem, + DcmTag& dcmTag, + const OFString& vr); + + /** extract DICOM tag from the given string + * @param keyToken token containing the string representation of the tag in form of "ggggeeee" + * @param tagkey stores the extracted DICOM tag + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition extractTag( + OFJsmnTokenPtr keyToken, + DcmTagKey& tagkey); + + /** helper function processing escaped characters in JSON strings + * @param value containing the string. The string will be changed + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition processJSONEscapeCharacters(OFString& value); + + /** parse the dataset part of an XML file containing a DICOM file or a DICOM dataset. + * @param dataset dataset stored in this parameter + * @param metaheader metaheader stored in this parameter + * @param current pointer to current JSMN Token + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition parseDataSet( + DcmItem* dataset, + DcmItem* metaheader, + OFJsmnTokenPtr& current); + + /** parse a DICOM sequence + * @param sequence DICOM Sequence + * @param current pointer to current token in the tokenArray + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition parseSequence( + DcmSequenceOfItems& sequence, + OFJsmnTokenPtr& current); + + /** parse a DICOM element that is not a sequence + * @param dataset dataset stored in this parameter + * @param metaheader metaheader stored in this parameter + * @param current pointer to current JSMN Token + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition parseElement( + DcmItem* dataset, + DcmItem* metaheader, + OFJsmnTokenPtr& current); + + /** parse a value array from JSON to DICOM + * @param newElem Pointer to element where the value should be stored + * @param current pointer to current token in the tokenArray + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition parseElementValueArray( + DcmElement*& newElem, + OFJsmnTokenPtr& current); + + /** parse a person name (PN value) + * @param value string containing the PN DICOM value + * @param current pointer to current token in the tokenArray + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition parsePersonName( + OFString& value, + OFJsmnTokenPtr& current); + + /** store decoded inline binary value in a DICOM element + * @param element element into which the value will be inserted + * @param data buffer containing the inline binary data + * @param length of the buffer + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition storeInlineBinaryValue( + DcmElement& element, + Uint8 *data, + size_t length); + + /** store loaded bulk data value in a DICOM element + * @param element element into which the value will be inserted + * @param data buffer containing the bulk data + * @param length of the buffer + * @return EC_Normal upon success, an error code otherwise + */ + virtual OFCondition storeBulkValue( + DcmElement& element, + Uint8 *data, + size_t length); + +}; + +#endif diff --git a/dcmdata/include/dcmtk/dcmdata/dclist.h b/dcmdata/include/dcmtk/dcmdata/dclist.h index 7a632fa3..a5bdebed 100644 --- a/dcmdata/include/dcmtk/dcmdata/dclist.h +++ b/dcmdata/include/dcmtk/dcmdata/dclist.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2021, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -34,20 +34,20 @@ const unsigned long DCM_EndOfListIndex = OFstatic_cast(unsigned long, -1L); /** helper class maintaining an entry in a DcmList double-linked list */ -class DCMTK_DCMDATA_EXPORT DcmListNode +class DCMTK_DCMDATA_EXPORT DcmListNode { public: /** constructor * @param obj object to be maintained by this list node */ - DcmListNode( DcmObject *obj ); + DcmListNode(DcmObject *obj); /// destructor ~DcmListNode(); /// return pointer to object maintained by this list node - inline DcmObject *value() { return objNodeValue; } + inline DcmObject *value() { return objNodeValue; } private: friend class DcmList; @@ -61,10 +61,10 @@ private: /// pointer to DcmObject instance maintained by this list entry DcmObject *objNodeValue; - /// private undefined copy constructor + /// private undefined copy constructor DcmListNode(const DcmListNode &); - /// private undefined copy assignment operator + /// private undefined copy assignment operator DcmListNode &operator=(const DcmListNode &); }; @@ -89,10 +89,10 @@ typedef enum } E_ListPos; /** double-linked list class that maintains pointers to DcmObject instances. - * The remove operation does not delete the object pointed to, however, - * the destructor will delete all elements pointed to + * The remove operation does not delete the object pointed to, however, the + * destructor will delete all elements the list points to. */ -class DCMTK_DCMDATA_EXPORT DcmList +class DCMTK_DCMDATA_EXPORT DcmList { public: /// constructor @@ -103,23 +103,23 @@ public: /** insert object at end of list * @param obj pointer to object - * @return pointer to object + * @return pointer to object, or NULL if object cannot be inserted */ - DcmObject *append( DcmObject *obj ); + DcmObject *append(DcmObject *obj); /** insert object at start of list * @param obj pointer to object - * @return pointer to object + * @return pointer to object, or NULL if object cannot be inserted */ - DcmObject *prepend( DcmObject *obj ); + DcmObject *prepend(DcmObject *obj); /** insert object relative to current position and indicator * @param obj pointer to object * @param pos position indicator - * @return pointer to object + * @return pointer to object, or NULL if object cannot be inserted */ - DcmObject *insert( DcmObject *obj, - E_ListPos pos = ELP_next ); + DcmObject *insert(DcmObject *obj, + const E_ListPos pos = ELP_next); /** remove current entry from list, return element * @return pointer to removed element, which is not deleted @@ -130,36 +130,36 @@ public: * @param pos position indicator * @return pointer to object */ - DcmObject *get( E_ListPos pos = ELP_atpos ); + DcmObject *get(const E_ListPos pos = ELP_atpos); /** seek within element in list to given position * (i.e. set current element to given position) * @param pos position indicator * @return pointer to new current object */ - DcmObject *seek( E_ListPos pos = ELP_next ); + DcmObject *seek(const E_ListPos pos = ELP_next); /** seek within element in list to given element index * (i.e. set current element to given index) * @param absolute_position position index < card() * @return pointer to new current object */ - DcmObject *seek_to(unsigned long absolute_position); + DcmObject *seek_to(const unsigned long absolute_position); - /** Remove and delete all elements from list. Thus, the - * elements' memory is also freed by this operation. The list - * is empty after calling this function. - */ + /** remove and delete all elements from list. Thus, the elements' memory + * is also freed by this operation. The list is empty after calling this + * function. + */ void deleteAllElements(); /// return cardinality of list inline unsigned long card() const { return cardinality; } /// return true if list is empty, false otherwise - inline OFBool empty(void) const { return firstNode == NULL; } + inline OFBool empty() const { return firstNode == NULL; } /// return true if current node exists, false otherwise - inline OFBool valid(void) const { return currentNode != NULL; } + inline OFBool valid() const { return currentNode != NULL; } private: /// pointer to first node in list @@ -171,10 +171,15 @@ private: /// pointer to current node in list DcmListNode *currentNode; + /// current position in list. + /// The position is maintained in order to avoid O(n) lookup + /// when essentially iterating the elements using seek_to(). + unsigned long currentPosition; + /// number of elements in list unsigned long cardinality; - - /// private undefined copy constructor + + /// private undefined copy constructor DcmList &operator=(const DcmList &); /** private undefined copy assignment operator diff --git a/dcmdata/include/dcmtk/dcmdata/dcmetinf.h b/dcmdata/include/dcmtk/dcmdata/dcmetinf.h index 8d7766a3..980cf179 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcmetinf.h +++ b/dcmdata/include/dcmtk/dcmdata/dcmetinf.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -197,6 +197,12 @@ class DCMTK_DCMDATA_EXPORT DcmMetaInfo const E_GrpLenEncoding groupLength = EGL_noChange, const Uint32 maxReadLength = DCM_MaxReadLength); + /** access the file preamble after reading a DICOM file. + * @return pointer to a 132-byte array containing the DICOM file preamble + * (i.e. the unused first 128 bytes of a DICOM file) and the magic word + * ("DICM"). If no DICOM fileformat was loaded, array will be zeroed. + */ + virtual const char *getPreamble() const; private: diff --git a/dcmdata/include/dcmtk/dcmdata/dcmxml/xml2dcm.h b/dcmdata/include/dcmtk/dcmdata/dcmxml/xml2dcm.h index 352ea0ad..ec4b7f17 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcmxml/xml2dcm.h +++ b/dcmdata/include/dcmtk/dcmdata/dcmxml/xml2dcm.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2003-2024, OFFIS e.V. + * Copyright (C) 2003-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -29,6 +29,7 @@ #include "dcmtk/ofstd/ofcond.h" #include "dcmtk/ofstd/oftypes.h" +#include "dcmtk/ofstd/ofdiag.h" #include "dcmtk/dcmdata/dcxfer.h" #ifdef __ibmxl__ @@ -46,7 +47,12 @@ typedef xmlNode *xmlNodePtr; typedef struct _xmlDoc xmlDoc; typedef xmlDoc *xmlDocPtr; +// MacOS 15.5 defines some Clang specific pragmas in libxml header files. +// Suppress warnings caused by these pragmas when compiling with GCC. +#include DCMTK_DIAGNOSTIC_PUSH +#include DCMTK_DIAGNOSTIC_IGNORE_CLANG_PRAGMAS_ON_GCC #include +#include DCMTK_DIAGNOSTIC_POP class DcmElement; class DcmItem; diff --git a/dcmdata/include/dcmtk/dcmdata/dcpixel.h b/dcmdata/include/dcmtk/dcmdata/dcpixel.h index 057af8ca..d2417cb1 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcpixel.h +++ b/dcmdata/include/dcmtk/dcmdata/dcpixel.h @@ -180,6 +180,7 @@ private: /// in write function: pointer to current pixel sequence DcmPixelSequence * pixelSeqForWrite; + /** check if this element should be written unencapsulated, even though an * encapsulated transfer syntax is used. In other words, this checks if * this pixel data element is on the main level on the dataset or not. @@ -694,6 +695,20 @@ public: virtual OFCondition getDecompressedColorModel( DcmItem *dataset, OFString &decompressedColorModel); + + /** determines the effective value of BitsAllocated that a dataset will have + * after decompression of an image with the given values for bitsAllocated + * and bitsStored. This may differ from the bitsAllocated parameter for example + * if that value is not a multiple of 8. Returns zero if an image with the + * given parameters cannot be decoded. + * @param bitsAllocated current value of Bits Allocated + * @param bitsStored current value of Bits Stored + * @return effective value of BitsAllocated, 0 if no decompression possible + */ + virtual Uint16 decodedBitsAllocated( + Uint16 bitsAllocated, + Uint16 bitsStored) const; + }; #endif diff --git a/dcmdata/include/dcmtk/dcmdata/dcpixseq.h b/dcmdata/include/dcmtk/dcmdata/dcpixseq.h index d7fd0d71..07c6f719 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcpixseq.h +++ b/dcmdata/include/dcmtk/dcmdata/dcpixseq.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -212,6 +212,14 @@ public: virtual OFCondition writeXML(STD_NAMESPACE ostream &out, const size_t flags = 0); + /** write object in JSON format to a stream + * @param out output stream to which the JSON document is written + * @param format used to format and customize the output + * @return status, EC_Normal if successful, an error code otherwise + */ + virtual OFCondition writeJson(STD_NAMESPACE ostream &out, + DcmJsonFormat &format); + /** special write method for creation of digital signatures * @param outStream DICOM output stream * @param oxfer output transfer syntax diff --git a/dcmdata/include/dcmtk/dcmdata/dcrleccd.h b/dcmdata/include/dcmtk/dcmdata/dcrleccd.h index 77155b32..a4354b0d 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcrleccd.h +++ b/dcmdata/include/dcmtk/dcmdata/dcrleccd.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2011, OFFIS e.V. + * Copyright (C) 2002-2024, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -163,6 +163,19 @@ public: const E_TransferSyntax oldRepType, const E_TransferSyntax newRepType) const; + /** determines the effective value of BitsAllocated that a dataset will have + * after decompression of an image with the given values for bitsAllocated + * and bitsStored. This may differ from the bitsAllocated parameter for example + * if that value is not a multiple of 8. Returns zero if an image with the + * given parameters cannot be decoded with this codec. + * @param bitsAllocated current value of Bits Allocated + * @param bitsStored current value of Bits Stored + * @return value of BitsAllocated after decompression, 0 if no decompression possible + */ + virtual Uint16 decodedBitsAllocated( + Uint16 bitsAllocated, + Uint16 bitsStored) const; + /** determine color model of the decompressed image * @param fromParam representation parameter of current compressed * representation, may be NULL diff --git a/dcmdata/include/dcmtk/dcmdata/dcrlecce.h b/dcmdata/include/dcmtk/dcmdata/dcrlecce.h index 4e36cea1..398a8360 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcrlecce.h +++ b/dcmdata/include/dcmtk/dcmdata/dcrlecce.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2020, OFFIS e.V. + * Copyright (C) 2002-2024, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -165,6 +165,19 @@ public: const E_TransferSyntax oldRepType, const E_TransferSyntax newRepType) const; + /** determines the effective value of BitsAllocated that a dataset will have + * after decompression of an image with the given values for bitsAllocated + * and bitsStored. This may differ from the bitsAllocated parameter for example + * if that value is not a multiple of 8. Returns zero if an image with the + * given parameters cannot be decoded with this codec. + * @param bitsAllocated current value of Bits Allocated + * @param bitsStored current value of Bits Stored + * @return value of BitsAllocated after decompression, 0 if no decompression possible + */ + virtual Uint16 decodedBitsAllocated( + Uint16 bitsAllocated, + Uint16 bitsStored) const; + /** determine color model of the decompressed image * @param fromParam representation parameter of current compressed * representation, may be NULL diff --git a/dcmdata/include/dcmtk/dcmdata/dcspchrs.h b/dcmdata/include/dcmtk/dcmdata/dcspchrs.h index eee88e23..08330a35 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcspchrs.h +++ b/dcmdata/include/dcmtk/dcmdata/dcspchrs.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2011-2017, OFFIS e.V. + * Copyright (C) 2011-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -36,7 +36,7 @@ class DcmItem; /** A class for managing and converting between different DICOM character sets. * The conversion relies on the OFCharacterEncoding class, which again relies - * on an underlying character encoding library (e.g. libiconv or ICU). + * on an underlying character encoding library (e.g. oficonv or libiconv). * @note Please note that a current limitation is that only a single value is * allowed for the destination character set (i.e. no code extensions). Of * course, for the source character set, also multiple values are supported. @@ -52,19 +52,20 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet /** destructor */ - ~DcmSpecificCharacterSet(); + virtual ~DcmSpecificCharacterSet(); /** clear the internal state. This also forgets about the currently * selected character sets, so selectCharacterSet() has to be called again * before a string can be converted with convertString(). */ - void clear(); + virtual void clear(); /** query whether selectCharacterSet() has successfully been called for this * object, i.e.\ whether convertString() may be called. * @return OFTrue if selectCharacterSet() was successfully called before, * OFFalse if not (or clear() has been called in the meantime). */ + virtual #ifdef HAVE_CXX11 explicit #endif @@ -75,7 +76,7 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet * @return OFTrue if selectCharacterSet() must be called before using * convertString(), OFFalse if it has already been called. */ - OFBool operator!() const; + virtual OFBool operator!() const; /** get currently selected source DICOM character set(s). Please note that * the returned string can contain multiple values (defined terms separated @@ -85,7 +86,7 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet * @return currently selected source DICOM character set(s) or an empty * string if none is selected (identical to ASCII, which is the default) */ - const OFString &getSourceCharacterSet() const; + virtual const OFString &getSourceCharacterSet() const; /** get currently selected destination DICOM character set. Please note * that the returned string, which contains a defined term, is always @@ -93,7 +94,7 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet * @return currently selected destination DICOM character set or an empty * string if none is selected (identical to ASCII, which is the default) */ - const OFString &getDestinationCharacterSet() const; + virtual const OFString &getDestinationCharacterSet() const; /** get currently selected destination encoding, i.e.\ the name of the * character set as used by the underlying character encoding library for @@ -102,15 +103,15 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet * @return currently selected destination encoding or an empty string if * none is selected */ - const OFString &getDestinationEncoding() const; + virtual const OFString &getDestinationEncoding() const; /** @copydoc OFCharacterEncoding::getConversionFlags() */ - unsigned getConversionFlags() const; + virtual unsigned getConversionFlags() const; /** @copydoc OFCharacterEncoding::setConversionFlags() */ - OFCondition setConversionFlags(const unsigned flags); + virtual OFCondition setConversionFlags(const unsigned flags); /** select DICOM character sets for the input and output string, between * which subsequent calls of convertString() convert. The defined terms @@ -133,8 +134,8 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet * default value is "ISO_IR 192" (Unicode in UTF-8). * @return status, EC_Normal if successful, an error code otherwise */ - OFCondition selectCharacterSet(const OFString &fromCharset, - const OFString &toCharset = "ISO_IR 192"); + virtual OFCondition selectCharacterSet(const OFString &fromCharset, + const OFString &toCharset = "ISO_IR 192"); /** select DICOM character sets for the input and output string, between * which subsequent calls of convertString() convert. The source @@ -160,12 +161,16 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet * value is "ISO_IR 192" (Unicode in UTF-8). * @return status, EC_Normal if successful, an error code otherwise */ - OFCondition selectCharacterSet(DcmItem &dataset, - const OFString &toCharset = "ISO_IR 192"); + virtual OFCondition selectCharacterSet(DcmItem &dataset, + const OFString &toCharset = "ISO_IR 192"); /** convert the given string from the selected source character set(s) to * the selected destination character set. That means selectCharacterSet() * has to be called prior to this method. + * @note The conversion code does not perform a thorough validation of the + * string. For example, characters that are permitted in the source + * character set but forbidden in DICOM (such as byte positions 0x80-0x9F + * in ISO_IR 100) may be converted without warning or error. * @param fromString input string to be converted (using the currently * selected source character set) * @param toString reference to variable where the converted string @@ -177,15 +182,19 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet * always regarded as delimiters (see DICOM PS 3.5). * @return status, EC_Normal if successful, an error code otherwise */ - OFCondition convertString(const OFString &fromString, - OFString &toString, - const OFString &delimiters = ""); + virtual OFCondition convertString(const OFString &fromString, + OFString &toString, + const OFString &delimiters = ""); /** convert the given string from the selected source character set(s) to * the selected destination character set. That means selectCharacterSet() * has to be called prior to this method. Since the length of the input * string has to be specified explicitly, the string can contain more than * one NULL byte. + * @note The conversion code does not perform a thorough validation of the + * string. For example, characters that are permitted in the source + * character set but forbidden in DICOM (such as byte positions 0x80-0x9F + * in ISO_IR 100) may be converted without warning or error. * @param fromString input string to be converted (using the currently * selected character set) * @param fromLength length of the input string (number of bytes without @@ -199,10 +208,10 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet * always regarded as delimiters (see DICOM PS 3.5). * @return status, EC_Normal if successful, an error code otherwise */ - OFCondition convertString(const char *fromString, - const size_t fromLength, - OFString &toString, - const OFString &delimiters = ""); + virtual OFCondition convertString(const char *fromString, + const size_t fromLength, + OFString &toString, + const OFString &delimiters = ""); // --- static helper functions --- @@ -233,7 +242,7 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet * output string * @return status, EC_Normal if successful, an error code otherwise */ - OFCondition determineDestinationEncoding(const OFString &toCharset); + virtual OFCondition determineDestinationEncoding(const OFString &toCharset); /** select a particular DICOM character set without code extensions for * subsequent conversions. The corresponding DICOM defined term for the @@ -241,7 +250,7 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet * 'SourceCharacterSet'. * @return status, EC_Normal if successful, an error code otherwise */ - OFCondition selectCharacterSetWithoutCodeExtensions(); + virtual OFCondition selectCharacterSetWithoutCodeExtensions(); /** select a particular DICOM character set with code extensions for * subsequent conversions. The corresponding DICOM defined terms for the @@ -252,8 +261,39 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet * already been determined by the calling method. * @return status, EC_Normal if successful, an error code otherwise */ - OFCondition selectCharacterSetWithCodeExtensions(const unsigned long sourceVM); + virtual OFCondition selectCharacterSetWithCodeExtensions(const unsigned long sourceVM); + + /** convert the given string from the selected source character set (without + * code extensions) to the selected destination character set + * @param fromString input string to be converted + * @param fromLength length of the input string (in bytes) + * @param toString reference to variable where to store the converted + * string + * @param delimiters string of characters regarded as delimiters + * @return status, EC_Normal if successful, an error code otherwise + */ + virtual OFCondition convertStringWithoutCodeExtensions(const char *fromString, + const size_t fromLength, + OFString &toString, + const OFString &delimiters); + /** convert the given string from the selected source character set(s) to + * the selected destination character set. This method supports code + * extension techniques according to ISO 2022 for the input string. + * @param fromString input string to be converted + * @param fromLength length of the input string (in bytes) + * @param toString reference to variable where to store the + * converted string + * @param delimiters string of characters regarded as delimiters + * @param hasEscapeChar flag indicating wether the input string contains + * one or more escape characters (ESC) + * @return status, EC_Normal if successful, an error code otherwise + */ + virtual OFCondition convertStringWithCodeExtensions(const char *fromString, + const size_t fromLength, + OFString &toString, + const OFString &delimiters, + const OFBool hasEscapeChar); /** check whether the given string contains at least one escape character * (ESC), because it is used for code extension techniques like ISO 2022 @@ -261,8 +301,8 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet * @param strLength length of the input string * @return OFTrue if an escape character has been found, OFFalse otherwise */ - OFBool checkForEscapeCharacter(const char *strValue, - const size_t strLength) const; + virtual OFBool checkForEscapeCharacter(const char *strValue, + const size_t strLength) const; /** convert given string to octal format, i.e.\ all non-ASCII and control * characters are converted to their octal representation. The total @@ -273,8 +313,8 @@ class DCMTK_DCMDATA_EXPORT DcmSpecificCharacterSet * @param strLength length of the input string * @return resulting string in octal format */ - OFString convertToLengthLimitedOctalString(const char *strValue, - const size_t strLength) const; + virtual OFString convertToLengthLimitedOctalString(const char *strValue, + const size_t strLength) const; private: diff --git a/dcmdata/include/dcmtk/dcmdata/dctypes.h b/dcmdata/include/dcmtk/dcmdata/dctypes.h index a3952a9a..dfe05699 100644 --- a/dcmdata/include/dcmtk/dcmdata/dctypes.h +++ b/dcmdata/include/dcmtk/dcmdata/dctypes.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2021, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -28,10 +28,8 @@ #include "dcmtk/dcmdata/dcdefine.h" BEGIN_EXTERN_C -#ifdef HAVE_SYS_TYPES_H /* needed e.g. on Solaris for definition of size_t */ #include -#endif END_EXTERN_C /* @@ -157,7 +155,7 @@ typedef enum { EWM_dataset = 1, /// write as fileformat and update required information (e.g. SOP Class/Instance UID) EWM_updateMeta = 2, - /// write as fileformat and create new meta header (do not retain existing information) + /// write as fileformat and create new meta header (do not retain existing information except SOP Class/Instance UID) EWM_createNewMeta = 3, /// write as fileformat but don't update the meta header (please be careful!) EWM_dontUpdateMeta = 4 @@ -173,7 +171,7 @@ struct DCMTK_DCMDATA_EXPORT DCMTypes /** @name print() flags. * These flags can be combined and passed to the print() methods. */ - //@{ + ///@{ /// shorten long tag values (e.g. long texts, pixel data) static const size_t PF_shortenLongTagValues; @@ -192,12 +190,12 @@ struct DCMTK_DCMDATA_EXPORT DCMTypes /// use ANSI escape codes for output static const size_t PF_useANSIEscapeCodes; - //@} + ///@} /** @name writeXML() flags. * These flags can be combined and passed to the writeXML() methods. */ - //@{ + ///@{ /// add document type definition (DTD). DCMTK-specific format only. static const size_t XF_addDocumentType; @@ -226,12 +224,12 @@ struct DCMTK_DCMDATA_EXPORT DCMTypes /// The default is to use the DCMTK-specific format. static const size_t XF_useNativeModel; - //@} + ///@} /** @name convertCharacterSet() flags. * These flags can be combined and passed to the convertCharacterSet() methods. */ - //@{ + ///@{ /// try to approximate characters that cannot be represented through similar /// looking characters. See DcmSpecificCharacterSet::getTransliterationMode(). @@ -241,7 +239,7 @@ struct DCMTK_DCMDATA_EXPORT DCMTypes /// See DcmSpecificCharacterSet::getDiscardIllegalSequenceMode(). static const size_t CF_discardIllegal; - //@} + ///@} }; diff --git a/dcmdata/include/dcmtk/dcmdata/dcuid.h b/dcmdata/include/dcmtk/dcmdata/dcuid.h index 769e5354..08a7b8e6 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcuid.h +++ b/dcmdata/include/dcmtk/dcmdata/dcuid.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -436,6 +436,8 @@ DCMTK_DCMDATA_EXPORT unsigned long dcmGuessModalityBytes(const char *sopClassUID #define UID_EncapsulatedUncompressedExplicitVRLittleEndianTransferSyntax "1.2.840.10008.1.2.1.98" /// Deflated Explicit VR Little Endian #define UID_DeflatedExplicitVRLittleEndianTransferSyntax "1.2.840.10008.1.2.1.99" +/// Deflated Image Frame Compression +#define UID_DeflatedImageFrameCompressionTransferSyntax "1.2.840.10008.1.2.8.1" /** JPEG Baseline (Process 1): Default Transfer Syntax * for Lossy JPEG 8 Bit Image Compression */ @@ -622,6 +624,8 @@ DCMTK_DCMDATA_EXPORT unsigned long dcmGuessModalityBytes(const char *sopClassUID #define UID_ElectrooculogramWaveformStorage "1.2.840.10008.5.1.4.1.1.9.7.3" #define UID_SleepElectroencephalogramWaveformStorage "1.2.840.10008.5.1.4.1.1.9.7.4" #define UID_BodyPositionWaveformStorage "1.2.840.10008.5.1.4.1.1.9.8.1" +#define UID_WaveformPresentationStateStorage "1.2.840.10008.5.1.4.1.1.9.100.1" +#define UID_WaveformAcquisitionPresentationStateStorage "1.2.840.10008.5.1.4.1.1.9.100.2" #define UID_RETIRED_StandaloneModalityLUTStorage "1.2.840.10008.5.1.4.1.1.10" #define UID_RETIRED_StandaloneVOILUTStorage "1.2.840.10008.5.1.4.1.1.11" #define UID_GrayscaleSoftcopyPresentationStateStorage "1.2.840.10008.5.1.4.1.1.11.1" @@ -777,6 +781,7 @@ DCMTK_DCMDATA_EXPORT unsigned long dcmGuessModalityBytes(const char *sopClassUID #define UID_DICONDE_EddyCurrentMultiframeImageStorage "1.2.840.10008.5.1.4.1.1.601.2" #define UID_DICONDE_ThermographyImageStorage "1.2.840.10008.5.1.4.1.1.601.3" #define UID_DICONDE_ThermographyMultiFrameImageStorage "1.2.840.10008.5.1.4.1.1.601.4" +#define UID_DICONDE_UltrasoundWaveformStorage "1.2.840.10008.5.1.4.1.1.601.5" // Query/Retrieve #define UID_FINDPatientRootQueryRetrieveInformationModel "1.2.840.10008.5.1.4.1.2.1.1" diff --git a/dcmdata/include/dcmtk/dcmdata/dcvrae.h b/dcmdata/include/dcmtk/dcmdata/dcvrae.h index df66f187..1c0c6646 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcvrae.h +++ b/dcmdata/include/dcmtk/dcmdata/dcvrae.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -102,8 +102,8 @@ class DCMTK_DCMDATA_EXPORT DcmApplicationEntity * @return status, EC_Normal if successful, an error code otherwise */ virtual OFCondition getOFString(OFString &stringVal, - const unsigned long pos, - OFBool normalize = OFTrue); + const unsigned long pos, + OFBool normalize = OFTrue); // ensure inherited overloads of matches take part in overload resolution using DcmByteString::matches; diff --git a/dcmdata/include/dcmtk/dcmdata/dcvrobow.h b/dcmdata/include/dcmtk/dcmdata/dcvrobow.h index fa0457c7..ed11020f 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcvrobow.h +++ b/dcmdata/include/dcmtk/dcmdata/dcvrobow.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2021, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -369,6 +369,22 @@ class DCMTK_DCMDATA_EXPORT DcmOtherByteOtherWord const char *pixelFileName, size_t *pixelCounter); + /** + * compares two binary values for equality. + * This function compares the binary data pointed to by `myValue` and `rhsValue` + * for equality up to the specified length `valLength`. See compare() method + * for more details. + * + * @param myValue pointer to the first value to compare. + * @param rhsValue pointer to the second value to compare. + * @param valLength the number of bytes to compare. + * @return an integer less than, equal to, or greater than zero if the first value + * is found to be less than, equal to, or greater than the second value. + */ + virtual int compareValues(const void* myValue, + const void* rhsValue, + const unsigned long valLength) const; + private: /** this flag is used during write operations and indicates that compact() should be diff --git a/dcmdata/include/dcmtk/dcmdata/dcxfer.h b/dcmdata/include/dcmtk/dcmdata/dcxfer.h index 93a7d688..16af105a 100644 --- a/dcmdata/include/dcmtk/dcmdata/dcxfer.h +++ b/dcmdata/include/dcmtk/dcmdata/dcxfer.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -88,72 +88,74 @@ typedef enum { EXS_RLELossless = 23, /// Deflated Explicit VR Little Endian EXS_DeflatedLittleEndianExplicit = 24, + /// Deflated Image Frame Compression + EXS_DeflatedImageFrameCompression = 25, /// JPEG-LS (lossless) - EXS_JPEGLSLossless = 25, + EXS_JPEGLSLossless = 26, /// JPEG-LS (lossless or near-lossless mode) - EXS_JPEGLSLossy = 26, + EXS_JPEGLSLossy = 27, /// JPEG 2000 (lossless) - EXS_JPEG2000LosslessOnly = 27, + EXS_JPEG2000LosslessOnly = 28, /// JPEG 2000 (lossless or lossy) - EXS_JPEG2000 = 28, + EXS_JPEG2000 = 29, /// JPEG 2000 part 2 multi-component extensions (lossless) - EXS_JPEG2000MulticomponentLosslessOnly = 29, + EXS_JPEG2000MulticomponentLosslessOnly = 30, /// JPEG 2000 part 2 multi-component extensions (lossless or lossy) - EXS_JPEG2000Multicomponent = 30, + EXS_JPEG2000Multicomponent = 31, /// JPIP Referenced - EXS_JPIPReferenced = 31, + EXS_JPIPReferenced = 32, /// JPIP Referenced Deflate - EXS_JPIPReferencedDeflate = 32, + EXS_JPIPReferencedDeflate = 33, /// MPEG2 Main Profile at Main Level - EXS_MPEG2MainProfileAtMainLevel = 33, + EXS_MPEG2MainProfileAtMainLevel = 34, /// Fragmentable MPEG2 Main Profile / Main Level - EXS_FragmentableMPEG2MainProfileMainLevel = 34, + EXS_FragmentableMPEG2MainProfileMainLevel = 35, /// MPEG2 Main Profile at High Level - EXS_MPEG2MainProfileAtHighLevel = 35, + EXS_MPEG2MainProfileAtHighLevel = 36, /// Fragmentable MPEG2 Main Profile / High Level - EXS_FragmentableMPEG2MainProfileHighLevel = 36, + EXS_FragmentableMPEG2MainProfileHighLevel = 37, /// MPEG4 High Profile / Level 4.1 - EXS_MPEG4HighProfileLevel4_1 = 37, + EXS_MPEG4HighProfileLevel4_1 = 38, /// Fragmentable MPEG4 High Profile / Level 4.1 - EXS_FragmentableMPEG4HighProfileLevel4_1 = 38, + EXS_FragmentableMPEG4HighProfileLevel4_1 = 39, /// MPEG4 BD-compatible High Profile / Level 4.1 - EXS_MPEG4BDcompatibleHighProfileLevel4_1 = 39, + EXS_MPEG4BDcompatibleHighProfileLevel4_1 = 40, /// Fragmentable MPEG4 BD-compatible High Profile / Level 4.1 - EXS_FragmentableMPEG4BDcompatibleHighProfileLevel4_1 = 40, + EXS_FragmentableMPEG4BDcompatibleHighProfileLevel4_1 = 41, /// MPEG4 High Profile / Level 4.2 For 2D Video - EXS_MPEG4HighProfileLevel4_2_For2DVideo = 41, + EXS_MPEG4HighProfileLevel4_2_For2DVideo = 42, /// Fragmentable MPEG4 High Profile / Level 4.2 For 2D Video - EXS_FragmentableMPEG4HighProfileLevel4_2_For2DVideo = 42, + EXS_FragmentableMPEG4HighProfileLevel4_2_For2DVideo = 43, /// MPEG4 High Profile / Level 4.2 For 3D Video - EXS_MPEG4HighProfileLevel4_2_For3DVideo = 43, + EXS_MPEG4HighProfileLevel4_2_For3DVideo = 44, /// Fragmentable MPEG4 Stereo High Profile / Level 4.2 - EXS_FragmentableMPEG4HighProfileLevel4_2_For3DVideo = 44, + EXS_FragmentableMPEG4HighProfileLevel4_2_For3DVideo = 45, /// MPEG4 Stereo High Profile / Level 4.2 - EXS_MPEG4StereoHighProfileLevel4_2 = 45, + EXS_MPEG4StereoHighProfileLevel4_2 = 46, /// Fragmentable HEVC/H.265 Main Profile / Level 5.1 - EXS_FragmentableMPEG4StereoHighProfileLevel4_2 = 46, + EXS_FragmentableMPEG4StereoHighProfileLevel4_2 = 47, /// HEVC/H.265 Main Profile / Level 5.1 - EXS_HEVCMainProfileLevel5_1 = 47, + EXS_HEVCMainProfileLevel5_1 = 48, /// HEVC/H.265 Main 10 Profile / Level 5.1 - EXS_HEVCMain10ProfileLevel5_1 = 48, + EXS_HEVCMain10ProfileLevel5_1 = 49, /// JPEG XL Lossless - EXS_JPEGXLLossless = 49, + EXS_JPEGXLLossless = 50, /// JPEG XL JPEG Recompression - EXS_JPEGXLJPEGRecompression = 50, + EXS_JPEGXLJPEGRecompression = 51, /// JPEG XL - EXS_JPEGXL = 51, + EXS_JPEGXL = 52, /// High-Throughput JPEG 2000 Image Compression (Lossless Only) - EXS_HighThroughputJPEG2000LosslessOnly = 52, + EXS_HighThroughputJPEG2000LosslessOnly = 53, /// High-Throughput JPEG 2000 with RPCL Options Image Compression (Lossless Only) - EXS_HighThroughputJPEG2000withRPCLOptionsLosslessOnly = 53, + EXS_HighThroughputJPEG2000withRPCLOptionsLosslessOnly = 54, /// High-Throughput JPEG 2000 Image Compression - EXS_HighThroughputJPEG2000 = 54, + EXS_HighThroughputJPEG2000 = 55, /// JPIP HTJ2K Referenced - EXS_JPIPHTJ2KReferenced = 55, + EXS_JPIPHTJ2KReferenced = 56, /// JPIP HTJ2K Referenced Deflate - EXS_JPIPHTJ2KReferencedDeflate = 56, + EXS_JPIPHTJ2KReferencedDeflate = 57, /// Private GE Little Endian Implicit with big endian pixel data - EXS_PrivateGE_LEI_WithBigEndianPixelData = 57 + EXS_PrivateGE_LEI_WithBigEndianPixelData = 58 } E_TransferSyntax; @@ -555,6 +557,23 @@ public: return isPixelDataLosslessCompressed() || isDatasetCompressed(); } + /** get DICOMweb MIME type for pixel data in this transfer syntax + * @return DICOMweb MIME type for pixel data in this transfer syntax + */ + inline const char *getMIMEType() const + { + return mimeType; + } + + /** get suggested filename extension to use when storing pixel data + * of this transfer syntax as bulk data for use with DICOMweb + * @return suggested filename extension + */ + inline const char *getFilenameExtension() const + { + return filenameExtension; + } + /** get the number of bytes needed to describe the tag, length, VR and any * reserved fields for this transfer syntax when encoding the specified VR. * @param evr value representation to be encoded in this transfer syntax @@ -601,6 +620,13 @@ private: /// validity of the transfer syntax definition (e.g. standard, retired, private) E_XferValidity xferValidity; + + /// MIME type used for this transfer syntax in DICOMweb + const char *mimeType; + + /// file name extension for storing bulk data in this transfer syntax + const char *filenameExtension; + }; /** global constant describing the byte order on the machine the application diff --git a/dcmdata/include/dcmtk/dcmdata/libi2d/i2d.h b/dcmdata/include/dcmtk/dcmdata/libi2d/i2d.h index e612dc75..b8b4a566 100644 --- a/dcmdata/include/dcmtk/dcmdata/libi2d/i2d.h +++ b/dcmdata/include/dcmtk/dcmdata/libi2d/i2d.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2022, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -45,7 +45,7 @@ public: /** Destructor, frees plugin memory */ - ~Image2Dcm(); + virtual ~Image2Dcm(); /** Start the conversion. Needs a fully configured input plugin * and a fully configured output plugin to operate. Returns @@ -79,6 +79,12 @@ public: I2DImgSource *inputPlug, size_t frameNumber); + /** Adjust byte order of the pixel data to local byte order for the OW VR. + * @param numberOfFrames - [in] The number of frames to be written + * @return EC_Normal, if successful, error otherwise + */ + virtual OFCondition adjustByteOrder(size_t numberOfFrames); + /** Update the offset table in the case of an encapsulated image * @return EC_Normal if offset table could be updated, error code otherwise */ @@ -197,6 +203,7 @@ protected: /** Reads pixel data and corresponding attributes like rows etc. from image * file and inserts them into dataset. * @param imageSource - [in] The input plugin that actually reads the pixel data + * @param outPlug - [in] The output plugin for specific SOP class output * @param numberOfFrames - [in] The number of frames to be written * @param dset - [out] The dataset to export the pixel data attributes to * @param outputTS - [out] The proposed transfex syntax of the dataset @@ -205,6 +212,7 @@ protected: */ OFCondition readAndInsertPixelDataFirstFrame( I2DImgSource* imageSource, + I2DOutputPlug *outPlug, size_t numberOfFrames, DcmDataset* dset, E_TransferSyntax& outputTS, diff --git a/dcmdata/include/dcmtk/dcmdata/libi2d/i2djpgs.h b/dcmdata/include/dcmtk/dcmdata/libi2d/i2djpgs.h index 8d15f2bc..d9ce86db 100644 --- a/dcmdata/include/dcmtk/dcmdata/libi2d/i2djpgs.h +++ b/dcmdata/include/dcmtk/dcmdata/libi2d/i2djpgs.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2007-2022, OFFIS e.V. + * Copyright (C) 2007-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -259,13 +259,15 @@ protected: * @param imageHeight - [out] The height of the image * @param samplesPerPixel - [out] Number of components per pixel * @param bitsPerSample - [out] Number of bits per pixel component + * @param colorModel - [out] color model of the JPEG bitstream * @return EC_Normal, if successful, error otherwise */ OFCondition getSOFImageParameters(const JPEGFileMapEntry& entry, Uint16& imageWidth, Uint16& imageHeight, Uint16& samplesPerPixel, - Uint16& bitsPerSample); + Uint16& bitsPerSample, + OFString& colorModel); /** Get image parameters as found at given SOF marker of the JPEG image. * Used for JPEG-LS codec. diff --git a/dcmdata/include/dcmtk/dcmdata/libi2d/i2doutpl.h b/dcmdata/include/dcmtk/dcmdata/libi2d/i2doutpl.h index 5fe90279..9e74e0b9 100644 --- a/dcmdata/include/dcmtk/dcmdata/libi2d/i2doutpl.h +++ b/dcmdata/include/dcmtk/dcmdata/libi2d/i2doutpl.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2022, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -26,6 +26,7 @@ #include "dcmtk/oflog/oflog.h" #include "dcmtk/ofstd/oflist.h" #include "dcmtk/ofstd/ofcond.h" +#include "dcmtk/dcmdata/dcxfer.h" #include "dcmtk/dcmdata/libi2d/i2define.h" extern DCMTK_I2D_EXPORT OFLogger DCM_dcmdataLibi2dLogger; @@ -86,6 +87,21 @@ public: */ virtual OFBool supportsMultiframe() const = 0; + /** checks if the output SOP class permits the given combination of + * transfer syntax and photometric interpretation + * @param photometricInterpretation - [in] photometric interpretation + * @param outputTS - [in] output transfer syntax + * @return true if combination permitted, false otherwise + */ + virtual OFBool colorModelPermitted(const OFString& photometricInterpretation, E_TransferSyntax outputTS) const = 0; + + /** change the photometric interpretation to the next best permitted one, for lenient mode + * @param photometricInterpretation - [in/out] photometric interpretation + * @param outputTS - [in] output transfer syntax + * @return EC_Normal if a "compatible" replacement exists, an error code otherwise + */ + virtual OFCondition updateColorModel(OFString& photometricInterpretation, E_TransferSyntax outputTS) const = 0; + /** Add multiframe specific attributes * @param targetDataset pointer to DICOM dataset, must not be NULL * @param numberOfFrames number of frames in this dataset diff --git a/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplnsc.h b/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplnsc.h index 36b321e5..e65c6639 100644 --- a/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplnsc.h +++ b/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplnsc.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2022, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -71,6 +71,21 @@ public: */ virtual OFBool supportsMultiframe() const; + /** checks if the output SOP class permits the given combination of + * transfer syntax and photometric interpretation + * @param photometricInterpretation - [in] photometric interpretation + * @param outputTS - [in] output transfer syntax + * @return true if combination permitted, false otherwise + */ + virtual OFBool colorModelPermitted(const OFString& photometricInterpretation, E_TransferSyntax outputTS) const; + + /** change the photometric interpretation to the next best permitted one, for lenient mode + * @param photometricInterpretation - [in/out] photometric interpretation + * @param outputTS - [in] output transfer syntax + * @return EC_Normal if a "compatible" replacement exists, an error code otherwise + */ + virtual OFCondition updateColorModel(OFString& photometricInterpretation, E_TransferSyntax outputTS) const; + /** Add multiframe specific attributes * @param targetDataset pointer to DICOM dataset, must not be NULL * @param numberOfFrames number of frames in this dataset diff --git a/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplop.h b/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplop.h index 89802979..d0f495cf 100644 --- a/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplop.h +++ b/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplop.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2022, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -70,6 +70,21 @@ public: */ virtual OFBool supportsMultiframe() const; + /** checks if the output SOP class permits the given combination of + * transfer syntax and photometric interpretation + * @param photometricInterpretation - [in] photometric interpretation + * @param outputTS - [in] output transfer syntax + * @return true if combination permitted, false otherwise + */ + virtual OFBool colorModelPermitted(const OFString& photometricInterpretation, E_TransferSyntax outputTS) const; + + /** change the photometric interpretation to the next best permitted one, for lenient mode + * @param photometricInterpretation - [in/out] photometric interpretation + * @param outputTS - [in] output transfer syntax + * @return EC_Normal if a "compatible" replacement exists, an error code otherwise + */ + virtual OFCondition updateColorModel(OFString& photometricInterpretation, E_TransferSyntax outputTS) const; + /** Add multiframe specific attributes * @param targetDataset pointer to DICOM dataset, must not be NULL * @param numberOfFrames number of frames in this dataset diff --git a/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplsc.h b/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplsc.h index 0a4edf58..1f51a819 100644 --- a/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplsc.h +++ b/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplsc.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2022, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -70,6 +70,21 @@ public: */ virtual OFBool supportsMultiframe() const; + /** checks if the output SOP class permits the given combination of + * transfer syntax and photometric interpretation + * @param photometricInterpretation - [in] photometric interpretation + * @param outputTS - [in] output transfer syntax + * @return true if combination permitted, false otherwise + */ + virtual OFBool colorModelPermitted(const OFString& photometricInterpretation, E_TransferSyntax outputTS) const; + + /** change the photometric interpretation to the next best permitted one, for lenient mode + * @param photometricInterpretation - [in/out] photometric interpretation + * @param outputTS - [in] output transfer syntax + * @return EC_Normal if a "compatible" replacement exists, an error code otherwise + */ + virtual OFCondition updateColorModel(OFString& photometricInterpretation, E_TransferSyntax outputTS) const; + /** Add multiframe specific attributes * @param targetDataset pointer to DICOM dataset, must not be NULL * @param numberOfFrames number of frames in this dataset diff --git a/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplvlp.h b/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplvlp.h index 4bbf3498..e4a323aa 100644 --- a/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplvlp.h +++ b/dcmdata/include/dcmtk/dcmdata/libi2d/i2dplvlp.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2022, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -70,6 +70,21 @@ public: */ virtual OFBool supportsMultiframe() const; + /** checks if the output SOP class permits the given combination of + * transfer syntax and photometric interpretation + * @param photometricInterpretation - [in] photometric interpretation + * @param outputTS - [in] output transfer syntax + * @return true if combination permitted, false otherwise + */ + virtual OFBool colorModelPermitted(const OFString& photometricInterpretation, E_TransferSyntax outputTS) const; + + /** change the photometric interpretation to the next best permitted one, for lenient mode + * @param photometricInterpretation - [in/out] photometric interpretation + * @param outputTS - [in] output transfer syntax + * @return EC_Normal if a "compatible" replacement exists, an error code otherwise + */ + virtual OFCondition updateColorModel(OFString& photometricInterpretation, E_TransferSyntax outputTS) const; + /** Add multiframe specific attributes * @param targetDataset pointer to DICOM dataset, must not be NULL * @param numberOfFrames number of frames in this dataset diff --git a/dcmdata/libdcxml/Makefile.dep b/dcmdata/libdcxml/Makefile.dep index bcf6e4ff..525bd328 100644 --- a/dcmdata/libdcxml/Makefile.dep +++ b/dcmdata/libdcxml/Makefile.dep @@ -3,61 +3,4 @@ xml2dcm.o: xml2dcm.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dcmxml/dcxmldf.h \ ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ ../../ofstd/include/dcmtk/ofstd/ofcast.h \ - ../../ofstd/include/dcmtk/ofstd/ofexport.h \ - ../../ofstd/include/dcmtk/ofstd/ofcond.h \ - ../../ofstd/include/dcmtk/ofstd/oftypes.h \ - ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ - ../../ofstd/include/dcmtk/ofstd/ofstring.h \ - ../../ofstd/include/dcmtk/ofstd/ofstream.h \ - ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ - ../../ofstd/include/dcmtk/ofstd/diag/push.def \ - ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ - ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ - ../include/dcmtk/dcmdata/dcxfer.h ../include/dcmtk/dcmdata/dctypes.h \ - ../../oflog/include/dcmtk/oflog/oflog.h \ - ../../oflog/include/dcmtk/oflog/logger.h \ - ../../oflog/include/dcmtk/oflog/config.h \ - ../../oflog/include/dcmtk/oflog/config/defines.h \ - ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ - ../../oflog/include/dcmtk/oflog/loglevel.h \ - ../../ofstd/include/dcmtk/ofstd/ofvector.h \ - ../../oflog/include/dcmtk/oflog/tstring.h \ - ../../oflog/include/dcmtk/oflog/tchar.h \ - ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ - ../../oflog/include/dcmtk/oflog/appender.h \ - ../../ofstd/include/dcmtk/ofstd/ofmem.h \ - ../../ofstd/include/dcmtk/ofstd/ofutil.h \ - ../../ofstd/include/dcmtk/ofstd/oftraits.h \ - ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ - ../../oflog/include/dcmtk/oflog/layout.h \ - ../../oflog/include/dcmtk/oflog/streams.h \ - ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ - ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ - ../../oflog/include/dcmtk/oflog/spi/filter.h \ - ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ - ../../oflog/include/dcmtk/oflog/spi/logfact.h \ - ../../oflog/include/dcmtk/oflog/logmacro.h \ - ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ - ../../oflog/include/dcmtk/oflog/tracelog.h \ - ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcvr.h \ - ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ - ../include/dcmtk/dcmdata/dcsequen.h \ - ../../ofstd/include/dcmtk/ofstd/offile.h \ - ../../ofstd/include/dcmtk/ofstd/ofstd.h \ - ../../ofstd/include/dcmtk/ofstd/oflist.h \ - ../../ofstd/include/dcmtk/ofstd/oflimits.h \ - ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../include/dcmtk/dcmdata/dcelem.h ../include/dcmtk/dcmdata/dcobject.h \ - ../include/dcmtk/dcmdata/dcerror.h ../include/dcmtk/dcmdata/dctag.h \ - ../include/dcmtk/dcmdata/dctagkey.h \ - ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ - ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dclist.h \ - ../include/dcmtk/dcmdata/dcitem.h ../include/dcmtk/dcmdata/dcpcache.h \ - ../include/dcmtk/dcmdata/dcpixseq.h ../include/dcmtk/dcmdata/dcofsetl.h \ - ../include/dcmtk/dcmdata/dcpixel.h ../include/dcmtk/dcmdata/dcvrpobw.h \ - ../include/dcmtk/dcmdata/dcvrobow.h ../include/dcmtk/dcmdata/dcpxitem.h \ - ../include/dcmtk/dcmdata/dcdeftag.h ../include/dcmtk/dcmdata/dcfilefo.h \ - ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcmetinf.h \ - ../include/dcmtk/dcmdata/dcswap.h + ../../ofstd/include/dcmtk/ofstd/ofexport.h diff --git a/dcmdata/libdcxml/xml2dcm.cc b/dcmdata/libdcxml/xml2dcm.cc index 6c134a3b..ae268f2f 100644 --- a/dcmdata/libdcxml/xml2dcm.cc +++ b/dcmdata/libdcxml/xml2dcm.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2003-2024, OFFIS e.V. + * Copyright (C) 2003-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -35,23 +35,24 @@ #include "dcmtk/dcmdata/dcfilefo.h" #include "dcmtk/dcmdata/dcmetinf.h" #include "dcmtk/dcmdata/dcswap.h" +#include "dcmtk/ofstd/ofdiag.h" + +// MacOS 15.5 defines some Clang specific pragmas in libxml header files. +// Suppress warnings caused by these pragmas when compiling with GCC. +#include DCMTK_DIAGNOSTIC_PUSH +#include DCMTK_DIAGNOSTIC_IGNORE_CLANG_PRAGMAS_ON_GCC #include +#include DCMTK_DIAGNOSTIC_POP // This function is also used in dcmsr, try to stay in sync! -#if defined(HAVE_VSNPRINTF) && defined(HAVE_PROTOTYPE_VSNPRINTF) extern "C" void xml2dcm_errorFunction(void * ctx, const char *msg, ...) { // Classic C requires us to declare variables at the beginning of the function. OFString &buffer = *OFstatic_cast(OFString*, ctx); -#else -extern "C" void xml2dcm_errorFunction(void * /* ctx */, const char *msg, ...) -{ -#endif if (!DCM_dcmdataLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL)) return; -#if defined(HAVE_VSNPRINTF) && defined(HAVE_PROTOTYPE_VSNPRINTF) // libxml calls us multiple times for one line of log output which would // result in garbled output. To avoid this, we buffer the output in a local // string in the caller which we get through our 'ctx' parameter. Then, we @@ -82,17 +83,6 @@ extern "C" void xml2dcm_errorFunction(void * /* ctx */, const char *msg, ...) pos = buffer.find('\n'); } -#else - // No vsnprint, but at least vfprintf. Output the messages directly to stderr. - va_list ap; - va_start(ap, msg); -#ifdef HAVE_PROTOTYPE_STD__VFPRINTF - std::vfprintf(stderr, msg, ap); -#else - vfprintf(stderr, msg, ap); -#endif - va_end(ap); -#endif } diff --git a/dcmdata/libi2d/Makefile.dep b/dcmdata/libi2d/Makefile.dep index 6a1fa7f0..c6dbd948 100644 --- a/dcmdata/libi2d/Makefile.dep +++ b/dcmdata/libi2d/Makefile.dep @@ -39,13 +39,13 @@ i2d.o: i2d.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ - ../include/dcmtk/dcmdata/libi2d/i2define.h \ - ../include/dcmtk/dcmdata/libi2d/i2dimgs.h \ ../include/dcmtk/dcmdata/dcxfer.h ../include/dcmtk/dcmdata/dctypes.h \ ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../include/dcmtk/dcmdata/libi2d/i2define.h \ + ../include/dcmtk/dcmdata/libi2d/i2dimgs.h \ ../include/dcmtk/dcmdata/dcpixel.h ../include/dcmtk/dcmdata/dcvrpobw.h \ ../include/dcmtk/dcmdata/dcvrobow.h ../include/dcmtk/dcmdata/dcelem.h \ ../include/dcmtk/dcmdata/dcobject.h ../include/dcmtk/dcmdata/dcerror.h \ @@ -62,6 +62,7 @@ i2d.o: i2d.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dcitem.h ../include/dcmtk/dcmdata/dcpcache.h \ ../include/dcmtk/dcmdata/dcdeftag.h ../include/dcmtk/dcmdata/dcuid.h \ ../include/dcmtk/dcmdata/dcpixseq.h ../include/dcmtk/dcmdata/dcpath.h \ + ../include/dcmtk/dcmdata/dcswap.h \ ../include/dcmtk/dcmdata/dcmxml/xml2dcm.h \ ../include/dcmtk/dcmdata/dcmxml/dcxmldf.h i2dbmps.o: i2dbmps.cc ../../config/include/dcmtk/config/osconfig.h \ @@ -210,19 +211,18 @@ i2doutpl.o: i2doutpl.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../include/dcmtk/dcmdata/dcxfer.h ../include/dcmtk/dcmdata/dctypes.h \ + ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ ../include/dcmtk/dcmdata/libi2d/i2define.h \ ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcitem.h \ ../../ofstd/include/dcmtk/ofstd/offile.h \ ../../ofstd/include/dcmtk/ofstd/ofstd.h \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../include/dcmtk/dcmdata/dctypes.h ../include/dcmtk/dcmdata/dcdefine.h \ - ../include/dcmtk/dcmdata/dcobject.h \ - ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../include/dcmtk/dcmdata/dcerror.h ../include/dcmtk/dcmdata/dcxfer.h \ - ../include/dcmtk/dcmdata/dcvr.h \ - ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../include/dcmtk/dcmdata/dcobject.h ../include/dcmtk/dcmdata/dcerror.h \ ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dctagkey.h \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dclist.h \ @@ -269,9 +269,13 @@ i2dplnsc.o: i2dplnsc.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../include/dcmtk/dcmdata/dcxfer.h ../include/dcmtk/dcmdata/dctypes.h \ + ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ ../include/dcmtk/dcmdata/libi2d/i2define.h \ ../include/dcmtk/dcmdata/dcdeftag.h ../include/dcmtk/dcmdata/dctagkey.h \ - ../include/dcmtk/dcmdata/dcdefine.h \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ ../include/dcmtk/dcmdata/dcuid.h ../include/dcmtk/dcmdata/dcdatset.h \ ../include/dcmtk/dcmdata/dcitem.h \ @@ -279,12 +283,7 @@ i2dplnsc.o: i2dplnsc.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofstd.h \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../include/dcmtk/dcmdata/dctypes.h ../include/dcmtk/dcmdata/dcobject.h \ - ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../include/dcmtk/dcmdata/dcerror.h ../include/dcmtk/dcmdata/dcxfer.h \ - ../include/dcmtk/dcmdata/dcvr.h \ - ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../include/dcmtk/dcmdata/dcobject.h ../include/dcmtk/dcmdata/dcerror.h \ ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dcstack.h \ ../include/dcmtk/dcmdata/dclist.h ../include/dcmtk/dcmdata/dcpcache.h i2dplop.o: i2dplop.cc ../../config/include/dcmtk/config/osconfig.h \ @@ -328,9 +327,13 @@ i2dplop.o: i2dplop.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../include/dcmtk/dcmdata/dcxfer.h ../include/dcmtk/dcmdata/dctypes.h \ + ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ ../include/dcmtk/dcmdata/libi2d/i2define.h \ ../include/dcmtk/dcmdata/dcdeftag.h ../include/dcmtk/dcmdata/dctagkey.h \ - ../include/dcmtk/dcmdata/dcdefine.h \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ ../include/dcmtk/dcmdata/dcuid.h \ ../../ofstd/include/dcmtk/ofstd/ofdatime.h \ @@ -341,12 +344,7 @@ i2dplop.o: i2dplop.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofstd.h \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../include/dcmtk/dcmdata/dctypes.h ../include/dcmtk/dcmdata/dcobject.h \ - ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../include/dcmtk/dcmdata/dcerror.h ../include/dcmtk/dcmdata/dcxfer.h \ - ../include/dcmtk/dcmdata/dcvr.h \ - ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../include/dcmtk/dcmdata/dcobject.h ../include/dcmtk/dcmdata/dcerror.h \ ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dcstack.h \ ../include/dcmtk/dcmdata/dclist.h ../include/dcmtk/dcmdata/dcpcache.h i2dplsc.o: i2dplsc.cc ../../config/include/dcmtk/config/osconfig.h \ @@ -390,9 +388,13 @@ i2dplsc.o: i2dplsc.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../include/dcmtk/dcmdata/dcxfer.h ../include/dcmtk/dcmdata/dctypes.h \ + ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ ../include/dcmtk/dcmdata/libi2d/i2define.h \ ../include/dcmtk/dcmdata/dcdeftag.h ../include/dcmtk/dcmdata/dctagkey.h \ - ../include/dcmtk/dcmdata/dcdefine.h \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ ../include/dcmtk/dcmdata/dcuid.h ../include/dcmtk/dcmdata/dcdatset.h \ ../include/dcmtk/dcmdata/dcitem.h \ @@ -400,12 +402,7 @@ i2dplsc.o: i2dplsc.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofstd.h \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../include/dcmtk/dcmdata/dctypes.h ../include/dcmtk/dcmdata/dcobject.h \ - ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../include/dcmtk/dcmdata/dcerror.h ../include/dcmtk/dcmdata/dcxfer.h \ - ../include/dcmtk/dcmdata/dcvr.h \ - ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../include/dcmtk/dcmdata/dcobject.h ../include/dcmtk/dcmdata/dcerror.h \ ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dcstack.h \ ../include/dcmtk/dcmdata/dclist.h ../include/dcmtk/dcmdata/dcpcache.h i2dplvlp.o: i2dplvlp.cc ../../config/include/dcmtk/config/osconfig.h \ @@ -449,9 +446,13 @@ i2dplvlp.o: i2dplvlp.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../include/dcmtk/dcmdata/dcxfer.h ../include/dcmtk/dcmdata/dctypes.h \ + ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ ../include/dcmtk/dcmdata/libi2d/i2define.h \ ../include/dcmtk/dcmdata/dcdeftag.h ../include/dcmtk/dcmdata/dctagkey.h \ - ../include/dcmtk/dcmdata/dcdefine.h \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ ../include/dcmtk/dcmdata/dcuid.h ../include/dcmtk/dcmdata/dcdatset.h \ ../include/dcmtk/dcmdata/dcitem.h \ @@ -459,11 +460,6 @@ i2dplvlp.o: i2dplvlp.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofstd.h \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../include/dcmtk/dcmdata/dctypes.h ../include/dcmtk/dcmdata/dcobject.h \ - ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../include/dcmtk/dcmdata/dcerror.h ../include/dcmtk/dcmdata/dcxfer.h \ - ../include/dcmtk/dcmdata/dcvr.h \ - ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../include/dcmtk/dcmdata/dcobject.h ../include/dcmtk/dcmdata/dcerror.h \ ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dcstack.h \ ../include/dcmtk/dcmdata/dclist.h ../include/dcmtk/dcmdata/dcpcache.h diff --git a/dcmdata/libi2d/i2d.cc b/dcmdata/libi2d/i2d.cc index 73897652..c24189bc 100644 --- a/dcmdata/libi2d/i2d.cc +++ b/dcmdata/libi2d/i2d.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2007-2024, OFFIS e.V. + * Copyright (C) 2007-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -25,12 +25,13 @@ #include "dcmtk/dcmdata/libi2d/i2d.h" #include "dcmtk/ofstd/ofstd.h" #include "dcmtk/dcmdata/dcpxitem.h" -#include "dcmtk/dcmdata/dcfilefo.h" /* for DcmFileFormat */ -#include "dcmtk/dcmdata/dcdeftag.h" /* for DCM_ defines */ -#include "dcmtk/dcmdata/dcuid.h" /* for SITE_SERIES_UID_ROOT */ -#include "dcmtk/dcmdata/dcpixseq.h" /* for DcmPixelSequence */ -#include "dcmtk/dcmdata/dcpath.h" /* for override keys */ -#include "dcmtk/dcmdata/dcmxml/xml2dcm.h" /* for DcmXMLParseHelper */ +#include "dcmtk/dcmdata/dcfilefo.h" /* for DcmFileFormat */ +#include "dcmtk/dcmdata/dcdeftag.h" /* for DCM_ defines */ +#include "dcmtk/dcmdata/dcuid.h" /* for SITE_SERIES_UID_ROOT */ +#include "dcmtk/dcmdata/dcpixseq.h" /* for DcmPixelSequence */ +#include "dcmtk/dcmdata/dcpath.h" /* for override keys */ +#include "dcmtk/dcmdata/dcswap.h" /* for swapIfNecessary() */ +#include "dcmtk/dcmdata/dcmxml/xml2dcm.h" /* for DcmXMLParseHelper */ OFLogger DCM_dcmdataLibi2dLogger = OFLog::getLogger("dcmtk.dcmdata.libi2d"); @@ -147,7 +148,7 @@ OFCondition Image2Dcm::convertFirstFrame( // Read and insert pixel data m_compressionRatio = 1.0; - cond = readAndInsertPixelDataFirstFrame(inputPlug, numberOfFrames, tempDataset.get(), proposedTS, m_compressionRatio); + cond = readAndInsertPixelDataFirstFrame(inputPlug, outPlug, numberOfFrames, tempDataset.get(), proposedTS, m_compressionRatio); if (cond.bad()) { return cond; @@ -584,6 +585,7 @@ OFCondition Image2Dcm::insertEncapsulatedPixelDataNextFrame( OFCondition Image2Dcm::readAndInsertPixelDataFirstFrame( I2DImgSource* imgSource, + I2DOutputPlug *outPlug, size_t numberOfFrames, DcmDataset* dset, E_TransferSyntax& outputTS, @@ -652,6 +654,22 @@ OFCondition Image2Dcm::readAndInsertPixelDataFirstFrame( if (cond.bad()) return cond; + if (! outPlug->colorModelPermitted(m_photometricInterpretation, outputTS)) + { + OFString old_photometricInterpretation = m_photometricInterpretation; + cond = outPlug->updateColorModel(m_photometricInterpretation, outputTS); + DcmXfer xf(outputTS); + if (cond.good()) + { + DCMDATA_LIBI2D_WARN("Image2Dcm: photometric interpretation '" << old_photometricInterpretation << "' not permitted for the selected SOP class in '" << xf.getXferName() << "' transfer syntax, using '" << m_photometricInterpretation << "' instead"); + } + else + { + DCMDATA_LIBI2D_ERROR("Image2Dcm: photometric interpretation '" << old_photometricInterpretation << "' not permitted for the selected SOP class in '" << xf.getXferName() << "' transfer syntax"); + return cond; + } + } + cond = dset->putAndInsertOFStringArray(DCM_PhotometricInterpretation, m_photometricInterpretation); if (cond.bad()) return cond; @@ -1040,3 +1058,22 @@ OFCondition Image2Dcm::updateOffsetTable() if (m_offsetTable) result = m_offsetTable->createOffsetTable(m_offsetList); return result; } + + +OFCondition Image2Dcm::adjustByteOrder(size_t numberOfFrames) +{ + if (m_output_buffer) + { + // unencapsulated pixel data, byte swapping may be necessary + if (m_bitsAllocated < 9) + { + size_t bufSize = m_frameLength * numberOfFrames; + if (bufSize & 1) ++bufSize; + if (bufSize > 1) + { + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, m_output_buffer, OFstatic_cast(Uint32, bufSize), sizeof(Uint16)); + } + } + } + return EC_Normal; +} diff --git a/dcmdata/libi2d/i2djpgs.cc b/dcmdata/libi2d/i2djpgs.cc index e9e9c5c7..001e8cd6 100644 --- a/dcmdata/libi2d/i2djpgs.cc +++ b/dcmdata/libi2d/i2djpgs.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2007-2024, OFFIS e.V. + * Copyright (C) 2007-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -199,7 +199,7 @@ OFCondition I2DJpegSource::readPixelData(Uint16& rows, // Extract width, height, samples per pixel, bits per sample Uint16 width, height, spp, bps; - cond = getSOFImageParameters(**entry, width, height, spp, bps); + cond = getSOFImageParameters(**entry, width, height, spp, bps, photoMetrInt); if (cond.bad()) { closeFile(); @@ -258,36 +258,27 @@ OFCondition I2DJpegSource::readPixelData(Uint16& rows, cols = width; samplesPerPixel = spp; bitsStored = bps; - bitsAlloc = bps; - // When BitsStored = 12, we use BitsAllocated = 16 - if (bitsAlloc == 12) - { - bitsAlloc = 16; - } + // bitsAlloc is always the next largest multiple of 8 + if (bitsStored <= 8) + bitsAlloc = 8; + else bitsAlloc = 16; // HighBit is always BitsStored - 1. - highBit = bitsStored; - highBit--; + highBit = bitsStored -1; - if (samplesPerPixel == 1) - photoMetrInt = "MONOCHROME2"; - else if (samplesPerPixel == 3) + if ((samplesPerPixel != 1) && (samplesPerPixel != 3)) { - if (m_isJPEGLS) - photoMetrInt = "RGB"; - else - photoMetrInt = "YBR_FULL_422"; - } - else return makeOFCondition(OFM_dcmdata, 18, OF_error, "For JPEG data, Samples per Pixel must be 1 or 3"); + } + // Planar Configuration and Pixel Representation is always 0 for JPEG data planConf = 0; pixelRepr = 0; Uint32 tLength = 0; char* tPixelData = NULL; - cond = extractRawJPEGStream(tPixelData, tLength); + cond = extractRawJPEGStream(tPixelData, tLength); if (cond.bad()) { closeFile(); @@ -320,7 +311,8 @@ OFCondition I2DJpegSource::getSOFImageParameters(const JPEGFileMapEntry& entry, Uint16& imageWidth, Uint16& imageHeight, Uint16& samplesPerPixel, - Uint16& bitsPerSample) + Uint16& bitsPerSample, + OFString& colorModel) { DCMDATA_LIBI2D_DEBUG("I2DJpegSource: Examining JPEG SOF image parameters"); if (!isSOFMarker(entry.marker, m_isJPEGLS)) @@ -369,9 +361,79 @@ OFCondition I2DJpegSource::getSOFImageParameters(const JPEGFileMapEntry& entry, if (length != OFstatic_cast(unsigned int, 8 + num_components * 3)) return makeOFCondition(OFM_dcmdata, 18, OF_error, "Bogus SOF marker length"); + if (samplesPerPixel == 1) + { + colorModel = "MONOCHROME2"; + } + else if (samplesPerPixel == 3) + { + if (m_isJPEGLS) + { + colorModel = "RGB"; + } + else + { + // This is a lossy color JPEG file. + // Read component IDs and sampling factors + Uint8 i1=0, i2=0, i3=0, ss1=0, ss2=0, ss3=0, n=0; + result = read1Byte(i1); + if (result != EOF) result = read1Byte(ss1); + if (result != EOF) result = read1Byte(n); + if (result != EOF) result = read1Byte(i2); + if (result != EOF) result = read1Byte(ss2); + if (result != EOF) result = read1Byte(n); + if (result != EOF) result = read1Byte(i3); + if (result != EOF) result = read1Byte(ss3); + if (result == EOF) + return makeOFCondition(OFM_dcmdata, 18, OF_error, "Premature EOF in JPEG file"); + if (ss1 == 0x11 && ss2 == 0x11 && ss3 == 0x11) + { + DCMDATA_LIBI2D_DEBUG("I2DJpegSource: No subsampling"); + if (i1 == 'R' && i2 == 'G' && i3 == 'B') + { + // an Adobe RGB JPEG + colorModel = "RGB"; + } + else + { + // DICOM CP 1654 requires "YBR_FULL_422" to be used for lossy JPEG + // independent from the actual subsampling in use. Therefore, we use + // "YBR_FULL_422" and not "YBR_FULL". See DICOM Part 5, Table 8.2.1-1. + colorModel = "YBR_FULL_422"; + } + } + else if (ss1 == 0x21 && ss2 == 0x11 && ss3 == 0x11) + { + DCMDATA_LIBI2D_DEBUG("I2DJpegSource: 4:2:2 subsampling"); + colorModel = "YBR_FULL_422"; + } + else if (ss1 == 0x22 && ss2 == 0x11 && ss3 == 0x11) + { + DCMDATA_LIBI2D_DEBUG("I2DJpegSource: non-standard 4:2:0 subsampling"); + DCMDATA_LIBI2D_WARN("JPEG file contains non-standard 4:2:0 subsampling"); + colorModel = "YBR_FULL_422"; + } + else if (ss1 == 0x41 && ss2 == 0x11 && ss3 == 0x11) + { + DCMDATA_LIBI2D_DEBUG("I2DJpegSource: non-standard 4:1:1 subsampling"); + DCMDATA_LIBI2D_WARN("JPEG file contains non-standard 4:1:1 subsampling"); + colorModel = "YBR_FULL_422"; + } + else + { + DCMDATA_LIBI2D_DEBUG("I2DJpegSource: non-standard subsampling: " + << OFstatic_cast(unsigned int, ss1 >> 4) << "/" << OFstatic_cast(unsigned int, ss1 & 15) << ", " + << OFstatic_cast(unsigned int, ss2 >> 4) << "/" << OFstatic_cast(unsigned int, ss2 & 15) << ", " + << OFstatic_cast(unsigned int, ss3 >> 4) << "/" << OFstatic_cast(unsigned int, ss3 & 15)); + DCMDATA_LIBI2D_WARN("JPEG file contains non-standard subsampling"); + colorModel = "YBR_FULL_422"; + } + } + } return EC_Normal; } + OFCondition I2DJpegSource::getSOSImageParameters(const JPEGFileMapEntry& entry, Uint8& nearLossless) { diff --git a/dcmdata/libi2d/i2dplnsc.cc b/dcmdata/libi2d/i2dplnsc.cc index 183048e8..76e9e942 100644 --- a/dcmdata/libi2d/i2dplnsc.cc +++ b/dcmdata/libi2d/i2dplnsc.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2022, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -259,6 +259,151 @@ OFBool I2DOutputPlugNewSC::supportsMultiframe() const } +OFBool I2DOutputPlugNewSC::colorModelPermitted(const OFString& photometricInterpretation, E_TransferSyntax outputTS) const +{ + if (photometricInterpretation == "MONOCHROME2") return OFTrue; + + // DICOM part 3, section A.8.5.4: + // Photometric Interpretation (0028,0004) shall be RGB for uncompressed or lossless compressed Transfer Syntaxes that do not have defined color space transformations, + // YBR_ICT for irreversible JPEG 2000 Transfer Syntaxes, + // YBR_RCT for reversible JPEG 2000 Transfer Syntaxes, + // YBR_PARTIAL_420 for MPEG2, MPEG-4 AVC/H.264, HEVC/H.265 Transfer Syntaxes and + // YBR_FULL_422 for JPEG lossy compressed Transfer Syntaxes and + // YBR_FULL or RGB for RLE Transfer Syntaxes + switch(outputTS) + { + case EXS_JPEG2000: + case EXS_JPEG2000Multicomponent: + case EXS_HighThroughputJPEG2000: + return (photometricInterpretation == "YBR_ICT"); + /* break; */ + case EXS_JPEG2000LosslessOnly: + case EXS_JPEG2000MulticomponentLosslessOnly: + case EXS_HighThroughputJPEG2000LosslessOnly: + case EXS_HighThroughputJPEG2000withRPCLOptionsLosslessOnly: + return (photometricInterpretation == "YBR_RCT"); + /* break; */ + case EXS_MPEG2MainProfileAtMainLevel: + case EXS_FragmentableMPEG2MainProfileMainLevel: + case EXS_MPEG2MainProfileAtHighLevel: + case EXS_FragmentableMPEG2MainProfileHighLevel: + case EXS_MPEG4HighProfileLevel4_1: + case EXS_FragmentableMPEG4HighProfileLevel4_1: + case EXS_MPEG4BDcompatibleHighProfileLevel4_1: + case EXS_FragmentableMPEG4BDcompatibleHighProfileLevel4_1: + case EXS_MPEG4HighProfileLevel4_2_For2DVideo: + case EXS_FragmentableMPEG4HighProfileLevel4_2_For2DVideo: + case EXS_MPEG4HighProfileLevel4_2_For3DVideo: + case EXS_FragmentableMPEG4HighProfileLevel4_2_For3DVideo: + case EXS_MPEG4StereoHighProfileLevel4_2: + case EXS_FragmentableMPEG4StereoHighProfileLevel4_2: + case EXS_HEVCMainProfileLevel5_1: + case EXS_HEVCMain10ProfileLevel5_1: + return (photometricInterpretation == "YBR_PARTIAL_420"); + /* break; */ + case EXS_JPEGProcess1: + case EXS_JPEGProcess2_4: + case EXS_JPEGProcess3_5: + case EXS_JPEGProcess6_8: + case EXS_JPEGProcess7_9: + case EXS_JPEGProcess10_12: + case EXS_JPEGProcess11_13: + case EXS_JPEGProcess16_18: + case EXS_JPEGProcess17_19: + case EXS_JPEGProcess20_22: + case EXS_JPEGProcess21_23: + case EXS_JPEGProcess24_26: + case EXS_JPEGProcess25_27: + return (photometricInterpretation == "YBR_FULL_422"); + /* break; */ + case EXS_RLELossless: + return ((photometricInterpretation == "YBR_FULL") || (photometricInterpretation == "RGB")); + /* break; */ + case EXS_JPEGXLLossless: + case EXS_JPEGXLJPEGRecompression: + case EXS_JPEGXL: + // DICOM part 3 does not (yet) specify any requirements for this IOD and JPEG-XL. + return OFTrue; + /* break; */ + default: + return (photometricInterpretation == "RGB"); + /* break; */ + } +} + + +OFCondition I2DOutputPlugNewSC::updateColorModel(OFString& photometricInterpretation, E_TransferSyntax outputTS) const +{ + if (photometricInterpretation == "MONOCHROME2") return EC_Normal; + switch(outputTS) + { + case EXS_JPEG2000: + case EXS_JPEG2000Multicomponent: + case EXS_HighThroughputJPEG2000: + if (photometricInterpretation == "YBR_ICT") return EC_Normal; else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + case EXS_JPEG2000LosslessOnly: + case EXS_JPEG2000MulticomponentLosslessOnly: + case EXS_HighThroughputJPEG2000LosslessOnly: + case EXS_HighThroughputJPEG2000withRPCLOptionsLosslessOnly: + if (photometricInterpretation == "YBR_RCT") return EC_Normal; else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + case EXS_MPEG2MainProfileAtMainLevel: + case EXS_FragmentableMPEG2MainProfileMainLevel: + case EXS_MPEG2MainProfileAtHighLevel: + case EXS_FragmentableMPEG2MainProfileHighLevel: + case EXS_MPEG4HighProfileLevel4_1: + case EXS_FragmentableMPEG4HighProfileLevel4_1: + case EXS_MPEG4BDcompatibleHighProfileLevel4_1: + case EXS_FragmentableMPEG4BDcompatibleHighProfileLevel4_1: + case EXS_MPEG4HighProfileLevel4_2_For2DVideo: + case EXS_FragmentableMPEG4HighProfileLevel4_2_For2DVideo: + case EXS_MPEG4HighProfileLevel4_2_For3DVideo: + case EXS_FragmentableMPEG4HighProfileLevel4_2_For3DVideo: + case EXS_MPEG4StereoHighProfileLevel4_2: + case EXS_FragmentableMPEG4StereoHighProfileLevel4_2: + case EXS_HEVCMainProfileLevel5_1: + case EXS_HEVCMain10ProfileLevel5_1: + if (photometricInterpretation == "YBR_PARTIAL_420") return EC_Normal; else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + case EXS_JPEGProcess1: + case EXS_JPEGProcess2_4: + case EXS_JPEGProcess3_5: + case EXS_JPEGProcess6_8: + case EXS_JPEGProcess7_9: + case EXS_JPEGProcess10_12: + case EXS_JPEGProcess11_13: + case EXS_JPEGProcess16_18: + case EXS_JPEGProcess17_19: + case EXS_JPEGProcess20_22: + case EXS_JPEGProcess21_23: + case EXS_JPEGProcess24_26: + case EXS_JPEGProcess25_27: + // silently replace "YBR_FULL" by "YBR_FULL_422". Most JPEG codecs won't mind. + if (photometricInterpretation == "YBR_FULL") + { + photometricInterpretation = "YBR_FULL_422"; + return EC_Normal; + } + else if (photometricInterpretation == "YBR_FULL_422") return EC_Normal; + else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + case EXS_RLELossless: + if ((photometricInterpretation == "YBR_FULL") || (photometricInterpretation == "RGB")) return EC_Normal; else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + case EXS_JPEGXLLossless: + case EXS_JPEGXLJPEGRecompression: + case EXS_JPEGXL: + // DICOM part 3 does not (yet) specify any requirements for this IOD and JPEG-XL. + return EC_Normal; + /* break; */ + default: + if (photometricInterpretation == "RGB") return EC_Normal; else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + } +} + + OFCondition I2DOutputPlugNewSC::insertMultiFrameAttributes( DcmDataset* targetDataset, size_t numberOfFrames) const diff --git a/dcmdata/libi2d/i2dplop.cc b/dcmdata/libi2d/i2dplop.cc index 3ba7bf53..51931cd1 100644 --- a/dcmdata/libi2d/i2dplop.cc +++ b/dcmdata/libi2d/i2dplop.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2022, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -278,6 +278,146 @@ OFBool I2DOutputPlugOphthalmicPhotography::supportsMultiframe() const } +OFBool I2DOutputPlugOphthalmicPhotography::colorModelPermitted(const OFString& photometricInterpretation, E_TransferSyntax outputTS) const +{ + if (photometricInterpretation == "MONOCHROME2") return OFTrue; + + // DICOM part 3, C.8.17.2.1.3: + // When Samples per Pixel (0028,0002) is greater than 1, + // Photometric Interpretation (0028,0004) shall be RGB for uncompressed or lossless compressed Transfer Syntaxes that do not have defined color space transformations, + // YBR_ICT for irreversible JPEG 2000 Transfer Syntaxes, + // YBR_RCT for reversible JPEG 2000 Transfer Syntaxes, + // YBR_PARTIAL_420 for MPEG2, MPEG-4 AVC/H.264, HEVC/H.265 Transfer Syntaxes and + // YBR_FULL_422 for JPEG lossy compressed Transfer Syntaxes. + // Note that YBR_FULL is explicitly not permitted for RLE in this SOP class. + switch(outputTS) + { + case EXS_JPEG2000: + case EXS_JPEG2000Multicomponent: + case EXS_HighThroughputJPEG2000: + return (photometricInterpretation == "YBR_ICT"); + /* break; */ + case EXS_JPEG2000LosslessOnly: + case EXS_JPEG2000MulticomponentLosslessOnly: + case EXS_HighThroughputJPEG2000LosslessOnly: + case EXS_HighThroughputJPEG2000withRPCLOptionsLosslessOnly: + return (photometricInterpretation == "YBR_RCT"); + /* break; */ + case EXS_MPEG2MainProfileAtMainLevel: + case EXS_FragmentableMPEG2MainProfileMainLevel: + case EXS_MPEG2MainProfileAtHighLevel: + case EXS_FragmentableMPEG2MainProfileHighLevel: + case EXS_MPEG4HighProfileLevel4_1: + case EXS_FragmentableMPEG4HighProfileLevel4_1: + case EXS_MPEG4BDcompatibleHighProfileLevel4_1: + case EXS_FragmentableMPEG4BDcompatibleHighProfileLevel4_1: + case EXS_MPEG4HighProfileLevel4_2_For2DVideo: + case EXS_FragmentableMPEG4HighProfileLevel4_2_For2DVideo: + case EXS_MPEG4HighProfileLevel4_2_For3DVideo: + case EXS_FragmentableMPEG4HighProfileLevel4_2_For3DVideo: + case EXS_MPEG4StereoHighProfileLevel4_2: + case EXS_FragmentableMPEG4StereoHighProfileLevel4_2: + case EXS_HEVCMainProfileLevel5_1: + case EXS_HEVCMain10ProfileLevel5_1: + return (photometricInterpretation == "YBR_PARTIAL_420"); + /* break; */ + case EXS_JPEGProcess1: + case EXS_JPEGProcess2_4: + case EXS_JPEGProcess3_5: + case EXS_JPEGProcess6_8: + case EXS_JPEGProcess7_9: + case EXS_JPEGProcess10_12: + case EXS_JPEGProcess11_13: + case EXS_JPEGProcess16_18: + case EXS_JPEGProcess17_19: + case EXS_JPEGProcess20_22: + case EXS_JPEGProcess21_23: + case EXS_JPEGProcess24_26: + case EXS_JPEGProcess25_27: + return (photometricInterpretation == "YBR_FULL_422"); + /* break; */ + case EXS_JPEGXLLossless: + case EXS_JPEGXLJPEGRecompression: + case EXS_JPEGXL: + // DICOM part 3 does not (yet) specify any requirements for this IOD and JPEG-XL. + return OFTrue; + /* break; */ + default: + return (photometricInterpretation == "RGB"); + /* break; */ + } +} + + +OFCondition I2DOutputPlugOphthalmicPhotography::updateColorModel(OFString& photometricInterpretation, E_TransferSyntax outputTS) const +{ + if (photometricInterpretation == "MONOCHROME2") return EC_Normal; + switch(outputTS) + { + case EXS_JPEG2000: + case EXS_JPEG2000Multicomponent: + case EXS_HighThroughputJPEG2000: + if (photometricInterpretation == "YBR_ICT") return EC_Normal; else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + case EXS_JPEG2000LosslessOnly: + case EXS_JPEG2000MulticomponentLosslessOnly: + case EXS_HighThroughputJPEG2000LosslessOnly: + case EXS_HighThroughputJPEG2000withRPCLOptionsLosslessOnly: + if (photometricInterpretation == "YBR_RCT") return EC_Normal; else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + case EXS_MPEG2MainProfileAtMainLevel: + case EXS_FragmentableMPEG2MainProfileMainLevel: + case EXS_MPEG2MainProfileAtHighLevel: + case EXS_FragmentableMPEG2MainProfileHighLevel: + case EXS_MPEG4HighProfileLevel4_1: + case EXS_FragmentableMPEG4HighProfileLevel4_1: + case EXS_MPEG4BDcompatibleHighProfileLevel4_1: + case EXS_FragmentableMPEG4BDcompatibleHighProfileLevel4_1: + case EXS_MPEG4HighProfileLevel4_2_For2DVideo: + case EXS_FragmentableMPEG4HighProfileLevel4_2_For2DVideo: + case EXS_MPEG4HighProfileLevel4_2_For3DVideo: + case EXS_FragmentableMPEG4HighProfileLevel4_2_For3DVideo: + case EXS_MPEG4StereoHighProfileLevel4_2: + case EXS_FragmentableMPEG4StereoHighProfileLevel4_2: + case EXS_HEVCMainProfileLevel5_1: + case EXS_HEVCMain10ProfileLevel5_1: + if (photometricInterpretation == "YBR_PARTIAL_420") return EC_Normal; else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + case EXS_JPEGProcess1: + case EXS_JPEGProcess2_4: + case EXS_JPEGProcess3_5: + case EXS_JPEGProcess6_8: + case EXS_JPEGProcess7_9: + case EXS_JPEGProcess10_12: + case EXS_JPEGProcess11_13: + case EXS_JPEGProcess16_18: + case EXS_JPEGProcess17_19: + case EXS_JPEGProcess20_22: + case EXS_JPEGProcess21_23: + case EXS_JPEGProcess24_26: + case EXS_JPEGProcess25_27: + // silently replace "YBR_FULL" by "YBR_FULL_422". Most JPEG codecs won't mind. + if (photometricInterpretation == "YBR_FULL") + { + photometricInterpretation = "YBR_FULL_422"; + return EC_Normal; + } + else if (photometricInterpretation == "YBR_FULL_422") return EC_Normal; + else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + case EXS_JPEGXLLossless: + case EXS_JPEGXLJPEGRecompression: + case EXS_JPEGXL: + // DICOM part 3 does not (yet) specify any requirements for this IOD and JPEG-XL. + return EC_Normal; + /* break; */ + default: + if (photometricInterpretation == "RGB") return EC_Normal; else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + } +} + + OFCondition I2DOutputPlugOphthalmicPhotography::insertMultiFrameAttributes( DcmDataset* targetDataset, size_t numberOfFrames) const diff --git a/dcmdata/libi2d/i2dplsc.cc b/dcmdata/libi2d/i2dplsc.cc index 51120056..b6e1bb49 100644 --- a/dcmdata/libi2d/i2dplsc.cc +++ b/dcmdata/libi2d/i2dplsc.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2021, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -81,6 +81,19 @@ OFBool I2DOutputPlugSC::supportsMultiframe() const } +OFBool I2DOutputPlugSC::colorModelPermitted(const OFString& /* photometricInterpretation */, E_TransferSyntax /* outputTS */) const +{ + // in secondary capture, no restrictions exist + return OFTrue; +} + + +OFCondition I2DOutputPlugSC::updateColorModel(OFString& /* photometricInterpretation */, E_TransferSyntax /* outputTS */) const +{ + return EC_Normal; +} + + OFCondition I2DOutputPlugSC::insertMultiFrameAttributes( DcmDataset* /* targetDataset */, size_t /* numberOfFrames */) const diff --git a/dcmdata/libi2d/i2dplvlp.cc b/dcmdata/libi2d/i2dplvlp.cc index f5074432..7a0a005d 100644 --- a/dcmdata/libi2d/i2dplvlp.cc +++ b/dcmdata/libi2d/i2dplvlp.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2022, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -134,6 +134,146 @@ OFBool I2DOutputPlugVLP::supportsMultiframe() const } +OFBool I2DOutputPlugVLP::colorModelPermitted(const OFString& photometricInterpretation, E_TransferSyntax outputTS) const +{ + if (photometricInterpretation == "MONOCHROME2") return OFTrue; + + // DICOM part 3, C.8.12.1.1.1: + // Photometric Interpretation (0028,0004) shall be RGB for uncompressed or lossless compressed Transfer Syntaxes that do not have defined color space transformations, + // YBR_ICT for irreversible JPEG 2000 Transfer Syntaxes, + // YBR_RCT for reversible JPEG 2000 Transfer Syntaxes, + // YBR_PARTIAL_420 for MPEG2, MPEG-4 AVC/H.264, HEVC/H.265 Transfer Syntaxes and + // YBR_FULL_422 for JPEG lossy compressed Transfer Syntaxes. + // Note that YBR_FULL is explicitly not permitted for RLE in this SOP class. + + switch(outputTS) + { + case EXS_JPEG2000: + case EXS_JPEG2000Multicomponent: + case EXS_HighThroughputJPEG2000: + return (photometricInterpretation == "YBR_ICT"); + /* break; */ + case EXS_JPEG2000LosslessOnly: + case EXS_JPEG2000MulticomponentLosslessOnly: + case EXS_HighThroughputJPEG2000LosslessOnly: + case EXS_HighThroughputJPEG2000withRPCLOptionsLosslessOnly: + return (photometricInterpretation == "YBR_RCT"); + /* break; */ + case EXS_MPEG2MainProfileAtMainLevel: + case EXS_FragmentableMPEG2MainProfileMainLevel: + case EXS_MPEG2MainProfileAtHighLevel: + case EXS_FragmentableMPEG2MainProfileHighLevel: + case EXS_MPEG4HighProfileLevel4_1: + case EXS_FragmentableMPEG4HighProfileLevel4_1: + case EXS_MPEG4BDcompatibleHighProfileLevel4_1: + case EXS_FragmentableMPEG4BDcompatibleHighProfileLevel4_1: + case EXS_MPEG4HighProfileLevel4_2_For2DVideo: + case EXS_FragmentableMPEG4HighProfileLevel4_2_For2DVideo: + case EXS_MPEG4HighProfileLevel4_2_For3DVideo: + case EXS_FragmentableMPEG4HighProfileLevel4_2_For3DVideo: + case EXS_MPEG4StereoHighProfileLevel4_2: + case EXS_FragmentableMPEG4StereoHighProfileLevel4_2: + case EXS_HEVCMainProfileLevel5_1: + case EXS_HEVCMain10ProfileLevel5_1: + return (photometricInterpretation == "YBR_PARTIAL_420"); + /* break; */ + case EXS_JPEGProcess1: + case EXS_JPEGProcess2_4: + case EXS_JPEGProcess3_5: + case EXS_JPEGProcess6_8: + case EXS_JPEGProcess7_9: + case EXS_JPEGProcess10_12: + case EXS_JPEGProcess11_13: + case EXS_JPEGProcess16_18: + case EXS_JPEGProcess17_19: + case EXS_JPEGProcess20_22: + case EXS_JPEGProcess21_23: + case EXS_JPEGProcess24_26: + case EXS_JPEGProcess25_27: + return (photometricInterpretation == "YBR_FULL_422"); + /* break; */ + case EXS_JPEGXLLossless: + case EXS_JPEGXLJPEGRecompression: + case EXS_JPEGXL: + // DICOM part 3 does not (yet) specify any requirements for this IOD and JPEG-XL. + return OFTrue; + /* break; */ + default: + return (photometricInterpretation == "RGB"); + /* break; */ + } +} + + +OFCondition I2DOutputPlugVLP::updateColorModel(OFString& photometricInterpretation, E_TransferSyntax outputTS) const +{ + if (photometricInterpretation == "MONOCHROME2") return EC_Normal; + switch(outputTS) + { + case EXS_JPEG2000: + case EXS_JPEG2000Multicomponent: + case EXS_HighThroughputJPEG2000: + if (photometricInterpretation == "YBR_ICT") return EC_Normal; else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + case EXS_JPEG2000LosslessOnly: + case EXS_JPEG2000MulticomponentLosslessOnly: + case EXS_HighThroughputJPEG2000LosslessOnly: + case EXS_HighThroughputJPEG2000withRPCLOptionsLosslessOnly: + if (photometricInterpretation == "YBR_RCT") return EC_Normal; else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + case EXS_MPEG2MainProfileAtMainLevel: + case EXS_FragmentableMPEG2MainProfileMainLevel: + case EXS_MPEG2MainProfileAtHighLevel: + case EXS_FragmentableMPEG2MainProfileHighLevel: + case EXS_MPEG4HighProfileLevel4_1: + case EXS_FragmentableMPEG4HighProfileLevel4_1: + case EXS_MPEG4BDcompatibleHighProfileLevel4_1: + case EXS_FragmentableMPEG4BDcompatibleHighProfileLevel4_1: + case EXS_MPEG4HighProfileLevel4_2_For2DVideo: + case EXS_FragmentableMPEG4HighProfileLevel4_2_For2DVideo: + case EXS_MPEG4HighProfileLevel4_2_For3DVideo: + case EXS_FragmentableMPEG4HighProfileLevel4_2_For3DVideo: + case EXS_MPEG4StereoHighProfileLevel4_2: + case EXS_FragmentableMPEG4StereoHighProfileLevel4_2: + case EXS_HEVCMainProfileLevel5_1: + case EXS_HEVCMain10ProfileLevel5_1: + if (photometricInterpretation == "YBR_PARTIAL_420") return EC_Normal; else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + case EXS_JPEGProcess1: + case EXS_JPEGProcess2_4: + case EXS_JPEGProcess3_5: + case EXS_JPEGProcess6_8: + case EXS_JPEGProcess7_9: + case EXS_JPEGProcess10_12: + case EXS_JPEGProcess11_13: + case EXS_JPEGProcess16_18: + case EXS_JPEGProcess17_19: + case EXS_JPEGProcess20_22: + case EXS_JPEGProcess21_23: + case EXS_JPEGProcess24_26: + case EXS_JPEGProcess25_27: + // silently replace "YBR_FULL" by "YBR_FULL_422". Most JPEG codecs won't mind. + if (photometricInterpretation == "YBR_FULL") + { + photometricInterpretation = "YBR_FULL_422"; + return EC_Normal; + } + else if (photometricInterpretation == "YBR_FULL_422") return EC_Normal; + else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + case EXS_JPEGXLLossless: + case EXS_JPEGXLJPEGRecompression: + case EXS_JPEGXL: + // DICOM part 3 does not (yet) specify any requirements for this IOD and JPEG-XL. + return EC_Normal; + /* break; */ + default: + if (photometricInterpretation == "RGB") return EC_Normal; else return makeOFCondition(OFM_dcmdata, 18, OF_error, "Unsupported color model"); + /* break; */ + } +} + + OFCondition I2DOutputPlugVLP::insertMultiFrameAttributes( DcmDataset* /* targetDataset */, size_t /* numberOfFrames */) const diff --git a/dcmdata/libsrc/CMakeLists.txt b/dcmdata/libsrc/CMakeLists.txt index 74e454cb..ddeac9c8 100644 --- a/dcmdata/libsrc/CMakeLists.txt +++ b/dcmdata/libsrc/CMakeLists.txt @@ -15,6 +15,7 @@ DCMTK_ADD_LIBRARY(dcmdata dcdict.cc dcdictbi.cc dcdirrec.cc + dcdocdec.cc dcelem.cc dcencdoc.cc dcerror.cc @@ -28,6 +29,7 @@ DCMTK_ADD_LIBRARY(dcmdata dcistrmz.cc dcitem.cc dcjson.cc + dcjsonrd.cc dclist.cc dcmatch.cc dcmetinf.cc diff --git a/dcmdata/libsrc/Makefile.dep b/dcmdata/libsrc/Makefile.dep index 92b7a993..ce421b04 100644 --- a/dcmdata/libsrc/Makefile.dep +++ b/dcmdata/libsrc/Makefile.dep @@ -658,6 +658,64 @@ dcdirrec.o: dcdirrec.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dcvrol.h ../include/dcmtk/dcmdata/dcvrov.h \ ../include/dcmtk/dcmdata/cmdlnarg.h ../include/dcmtk/dcmdata/dcspchrs.h \ ../../ofstd/include/dcmtk/ofstd/ofchrenc.h +dcdocdec.o: dcdocdec.cc ../../config/include/dcmtk/config/osconfig.h \ + ../include/dcmtk/dcmdata/dcdocdec.h ../include/dcmtk/dcmdata/dcdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../include/dcmtk/dcmdata/dcfilefo.h ../include/dcmtk/dcmdata/dcsequen.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../include/dcmtk/dcmdata/dcelem.h ../include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../include/dcmtk/dcmdata/dcerror.h ../include/dcmtk/dcmdata/dcxfer.h \ + ../include/dcmtk/dcmdata/dctypes.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dclist.h \ + ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcitem.h \ + ../include/dcmtk/dcmdata/dcpcache.h ../include/dcmtk/dcmdata/dcdeftag.h \ + ../include/dcmtk/dcmdata/dcuid.h dcelem.o: dcelem.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ ../../ofstd/include/dcmtk/ofstd/ofcast.h \ @@ -1322,10 +1380,107 @@ dcjson.o: dcjson.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dcdefine.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ - ../../ofstd/include/dcmtk/ofstd/diag/pop.def + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../ofstd/include/dcmtk/ofstd/ofsha256.h \ + ../include/dcmtk/dcmdata/dctypes.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../include/dcmtk/dcmdata/dcerror.h +dcjsonrd.o: dcjsonrd.cc ../../config/include/dcmtk/config/osconfig.h \ + ../include/dcmtk/dcmdata/dcjsonrd.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../include/dcmtk/dcmdata/dcdefine.h ../include/dcmtk/dcmdata/dcxfer.h \ + ../include/dcmtk/dcmdata/dctypes.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/ofjsmn.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../include/dcmtk/dcmdata/dcerror.h ../include/dcmtk/dcmdata/dcfilefo.h \ + ../include/dcmtk/dcmdata/dcsequen.h ../include/dcmtk/dcmdata/dcelem.h \ + ../include/dcmtk/dcmdata/dcobject.h ../include/dcmtk/dcmdata/dctag.h \ + ../include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dclist.h \ + ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcitem.h \ + ../include/dcmtk/dcmdata/dcpcache.h ../include/dcmtk/dcmdata/dcmetinf.h \ + ../include/dcmtk/dcmdata/dcdeftag.h ../include/dcmtk/dcmdata/dcswap.h \ + ../include/dcmtk/dcmdata/dcvrov.h ../include/dcmtk/dcmdata/dcvruv.h dclist.o: dclist.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofstream.h \ ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ ../include/dcmtk/dcmdata/dclist.h \ ../../ofstd/include/dcmtk/ofstd/ofcast.h \ ../../ofstd/include/dcmtk/ofstd/oftypes.h \ @@ -1977,6 +2132,7 @@ dcpixseq.o: dcpixseq.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofexport.h \ ../../ofstd/include/dcmtk/ofstd/ofstring.h \ ../../ofstd/include/dcmtk/ofstd/ofrand.h \ + ../../ofstd/include/dcmtk/ofstd/ofsha256.h \ ../include/dcmtk/dcmdata/dcpixseq.h ../include/dcmtk/dcmdata/dcsequen.h \ ../../ofstd/include/dcmtk/ofstd/offile.h \ ../../ofstd/include/dcmtk/ofstd/ofstd.h \ @@ -2025,7 +2181,8 @@ dcpixseq.o: dcpixseq.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dclist.h \ ../include/dcmtk/dcmdata/dcofsetl.h ../include/dcmtk/dcmdata/dcpxitem.h \ ../include/dcmtk/dcmdata/dcvrobow.h ../include/dcmtk/dcmdata/dcitem.h \ - ../include/dcmtk/dcmdata/dcpcache.h ../include/dcmtk/dcmdata/dcdeftag.h + ../include/dcmtk/dcmdata/dcpcache.h ../include/dcmtk/dcmdata/dcdeftag.h \ + ../include/dcmtk/dcmdata/dcjson.h dcpxitem.o: dcpxitem.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofstream.h \ ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ @@ -3462,6 +3619,7 @@ dcvrod.o: dcvrod.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../ofstd/include/dcmtk/ofstd/ofmath.h \ ../include/dcmtk/dcmdata/dcvrod.h ../include/dcmtk/dcmdata/dcvrfd.h \ ../include/dcmtk/dcmdata/dcelem.h ../include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ @@ -3518,6 +3676,7 @@ dcvrof.o: dcvrof.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../ofstd/include/dcmtk/ofstd/ofmath.h \ ../include/dcmtk/dcmdata/dcjson.h ../include/dcmtk/dcmdata/dctagkey.h \ ../include/dcmtk/dcmdata/dcdefine.h \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ diff --git a/dcmdata/libsrc/Makefile.in b/dcmdata/libsrc/Makefile.in index 4dd0b322..41ec5bbb 100644 --- a/dcmdata/libsrc/Makefile.in +++ b/dcmdata/libsrc/Makefile.in @@ -49,7 +49,7 @@ objs = dcpixseq.o dcpxitem.o dcuid.o dcerror.o dcencdoc.o\ dcvrut.o dcvrur.o dcvruc.o dctypes.o dcpcache.o dcddirif.o dcistrma.o \ dcistrmb.o dcistrmf.o dcistrms.o dcistrmz.o dcostrma.o dcostrmb.o \ dcostrmf.o dcostrms.o dcostrmz.o dcwcache.o dcpath.o vrscan.o vrscanl.o \ - dcfilter.o dcmatch.o dcjson.o + dcfilter.o dcmatch.o dcjson.o dcjsonrd.o dcdocdec.o support_objs = mkdeftag.o mkdictbi.o support_progs = mkdeftag mkdictbi diff --git a/dcmdata/libsrc/cmdlnarg.cc b/dcmdata/libsrc/cmdlnarg.cc index 56b17205..13ae9223 100644 --- a/dcmdata/libsrc/cmdlnarg.cc +++ b/dcmdata/libsrc/cmdlnarg.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2023, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -31,9 +31,7 @@ #endif BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include /* for O_BINARY */ -#endif END_EXTERN_C #include "dcmtk/ofstd/ofstd.h" diff --git a/dcmdata/libsrc/dcbytstr.cc b/dcmdata/libsrc/dcbytstr.cc index ae4a9b63..73f77375 100644 --- a/dcmdata/libsrc/dcbytstr.cc +++ b/dcmdata/libsrc/dcbytstr.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -603,7 +603,7 @@ OFCondition DcmByteString::makeMachineByteString(const Uint32 length) if (realLength > 0) { size_t i = OFstatic_cast(size_t, realLength); - while ((i > 0) && (value[i - 1] == paddingChar)) + while ((i > 0) && ((value[i - 1] == paddingChar) || (value[i - 1] == '\0'))) value[--i] = '\0'; realLength = OFstatic_cast(Uint32, i); } @@ -639,26 +639,19 @@ Uint8 *DcmByteString::newValueField() return NULL; } /* allocate space for extra padding character (required for the DICOM representation of the string) */ -#ifdef HAVE_STD__NOTHROW + // we want to use a non-throwing new here if available. // If the allocation fails, we report an EC_MemoryExhausted error // back to the caller. value = new (std::nothrow) Uint8[lengthField + 2]; -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - value = new Uint8[lengthField + 2]; - } - catch (STD_NAMESPACE bad_alloc const &) - { - value = NULL; - } -#endif /* terminate string after real length */ if (value != NULL) + { value[lengthField] = 0; + value[lengthField+1] = 0; + } + /* enforce old (pre DCMTK 3.5.2) behaviour? */ if (!dcmAcceptOddAttributeLength.get()) { @@ -668,22 +661,11 @@ Uint8 *DcmByteString::newValueField() } } else { /* length is even, but we need an extra byte for the terminating 0 byte */ -#ifdef HAVE_STD__NOTHROW + // we want to use a non-throwing new here if available. // If the allocation fails, we report an EC_MemoryExhausted error // back to the caller. value = new (std::nothrow) Uint8[lengthField + 1]; -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - value = new Uint8[lengthField + 1]; - } - catch (STD_NAMESPACE bad_alloc const &) - { - value = NULL; - } -#endif } /* make sure that the string is properly terminated by a 0 byte */ if (value != NULL) diff --git a/dcmdata/libsrc/dcchrstr.cc b/dcmdata/libsrc/dcchrstr.cc index 90c66fd4..a10d49d9 100644 --- a/dcmdata/libsrc/dcchrstr.cc +++ b/dcmdata/libsrc/dcchrstr.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -25,7 +25,7 @@ #include "dcmtk/dcmdata/dcspchrs.h" /* for class DcmSpecificCharacterSet */ #include "dcmtk/dcmdata/dcitem.h" /* for class DcmItem */ #include "dcmtk/dcmdata/dcdeftag.h" /* for tag definitions */ -#include "dcmtk/dcmdata/dcjson.h" /* json helper classes */ +#include "dcmtk/dcmdata/dcjson.h" /* JSON helper classes */ #include "dcmtk/dcmdata/dcmatch.h" // @@ -224,19 +224,23 @@ OFCondition DcmCharString::getSpecificCharacterSet(OFString &charset) OFCondition DcmCharString::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { + OFCondition result = EC_Normal; + /* always write JSON Opener */ DcmElement::writeJsonOpener(out, format); + /* write element value (if non-empty) */ if (!isEmpty()) { - OFString value; - if (format.asBulkDataURI(getTag(), value)) + if (format.asBulkDataURI(getTag(), getLength())) { - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, value); + /* adjust byte order to little endian */ + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); + result = format.writeBulkData(out, getTag(), getLengthField(), byteValues); } else { + OFString value; OFCondition status = getOFString(value, 0L); if (status.bad()) return status; @@ -254,10 +258,10 @@ OFCondition DcmCharString::writeJson(STD_NAMESPACE ostream &out, format.printValueSuffix(out); } } + /* write JSON Closer */ DcmElement::writeJsonCloser(out, format); - /* always report success */ - return EC_Normal; + return result; } diff --git a/dcmdata/libsrc/dccodec.cc b/dcmdata/libsrc/dccodec.cc index b0ac91bb..faf2f600 100644 --- a/dcmdata/libsrc/dccodec.cc +++ b/dcmdata/libsrc/dccodec.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1997-2023, OFFIS e.V. + * Copyright (C) 1997-2024, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -518,6 +518,40 @@ OFCondition DcmCodecList::decodeFrame( } +Uint16 DcmCodecList::decodedBitsAllocated( + const DcmXfer & fromType, + Uint16 bitsAllocated, + Uint16 bitsStored) +{ +#ifdef WITH_THREADS + if (! codecLock.initialized()) return 0; // should never happen +#endif + Uint16 result = 0; + + // acquire write lock on codec list. Will block if some write lock is currently active. +#ifdef WITH_THREADS + OFReadWriteLocker locker(codecLock); + if (0 == locker.rdlock()) + { +#endif + E_TransferSyntax fromXfer = fromType.getXfer(); + OFListIterator(DcmCodecList *) first = registeredCodecs.begin(); + OFListIterator(DcmCodecList *) last = registeredCodecs.end(); + while (first != last) + { + if ((*first)->codec->canChangeCoding(fromXfer, EXS_LittleEndianExplicit)) + { + result = (*first)->codec->decodedBitsAllocated(bitsAllocated, bitsStored); + first = last; + } else ++first; + } +#ifdef WITH_THREADS + } else result = 0; +#endif + return result; +} + + OFCondition DcmCodecList::encode( const E_TransferSyntax fromRepType, const DcmRepresentationParameter * fromParam, diff --git a/dcmdata/libsrc/dcdatset.cc b/dcmdata/libsrc/dcdatset.cc index 4ceb4a30..9292f139 100644 --- a/dcmdata/libsrc/dcdatset.cc +++ b/dcmdata/libsrc/dcdatset.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -471,11 +471,10 @@ OFCondition DcmDataset::readUntilTag(DcmInputStream &inStream, // ******************************** -OFCondition DcmDataset::write( - DcmOutputStream &outStream, - const E_TransferSyntax oxfer, - const E_EncodingType enctype /* = EET_UndefinedLength */, - DcmWriteCache *wcache) +OFCondition DcmDataset::write(DcmOutputStream &outStream, + const E_TransferSyntax oxfer, + const E_EncodingType enctype /* = EET_UndefinedLength */, + DcmWriteCache *wcache) { return write(outStream, oxfer, enctype, wcache, EGL_recalcGL); } @@ -919,3 +918,11 @@ OFCondition DcmDataset::doPostReadChecks() return result; } + +// ******************************** + +void DcmDataset::initializeXfer(const E_TransferSyntax xfer) +{ + OriginalXfer = xfer; + CurrentXfer = xfer; +} diff --git a/dcmdata/libsrc/dcddirif.cc b/dcmdata/libsrc/dcddirif.cc index b1d30015..8980771b 100644 --- a/dcmdata/libsrc/dcddirif.cc +++ b/dcmdata/libsrc/dcddirif.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2024, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -655,6 +655,9 @@ OFString DicomDirInterface::recordTypeToName(const E_DirRecType recordType) case ERT_Inventory: recordName = "Inventory"; break; + case ERT_WfPresentation: + recordName = "WfPresentation"; + break; default: recordName = "(unknown-directory-record-type)"; break; @@ -842,6 +845,11 @@ static E_DirRecType sopClassToRecordType(const OFString &sopClass) result = ERT_Annotation; else if (compare(sopClass, UID_InventoryStorage)) result = ERT_Inventory; + else if (compare(sopClass, UID_WaveformPresentationStateStorage) || + compare(sopClass, UID_WaveformAcquisitionPresentationStateStorage)) + { + result = ERT_WfPresentation; + } return result; } @@ -1032,6 +1040,7 @@ static OFCondition insertSortedUnder(DcmDirectoryRecord *parent, case ERT_Assessment: case ERT_Radiotherapy: case ERT_Annotation: + case ERT_WfPresentation: /* try to insert based on InstanceNumber */ result = insertWithISCriterion(parent, child, DCM_InstanceNumber); break; @@ -1107,6 +1116,8 @@ static OFBool isMultiframeStorageSOPClass(const OFString &sopClassUID) return compare(sopClassUID, UID_BreastProjectionXRayImageStorageForPresentation) || compare(sopClassUID, UID_BreastProjectionXRayImageStorageForProcessing) || compare(sopClassUID, UID_BreastTomosynthesisImageStorage) || +// in fact, the following IOD is a multi-frame image but the individual frames are rather "tiles" +// compare(sopClassUID, UID_ConfocalMicroscopyTiledPyramidalImageStorage) || compare(sopClassUID, UID_EnhancedContinuousRTImageStorage) || compare(sopClassUID, UID_EnhancedCTImageStorage) || compare(sopClassUID, UID_EnhancedMRColorImageStorage) || @@ -1116,8 +1127,10 @@ static OFBool isMultiframeStorageSOPClass(const OFString &sopClassUID) compare(sopClassUID, UID_EnhancedUSVolumeStorage) || compare(sopClassUID, UID_EnhancedXAImageStorage) || compare(sopClassUID, UID_EnhancedXRFImageStorage) || +// compare(sopClassUID, UID_HeightMapSegmentationStorage) || compare(sopClassUID, UID_IntravascularOpticalCoherenceTomographyImageStorageForPresentation) || compare(sopClassUID, UID_IntravascularOpticalCoherenceTomographyImageStorageForProcessing) || +// compare(sopClassUID, UID_LabelMapSegmentationStorage) || compare(sopClassUID, UID_MultiframeGrayscaleByteSecondaryCaptureImageStorage) || compare(sopClassUID, UID_MultiframeGrayscaleWordSecondaryCaptureImageStorage) || compare(sopClassUID, UID_MultiframeSingleBitSecondaryCaptureImageStorage) || @@ -1127,8 +1140,9 @@ static OFBool isMultiframeStorageSOPClass(const OFString &sopClassUID) compare(sopClassUID, UID_OphthalmicPhotography16BitImageStorage) || compare(sopClassUID, UID_OphthalmicPhotography8BitImageStorage) || compare(sopClassUID, UID_OphthalmicTomographyImageStorage) || - compare(sopClassUID, UID_ParametricMapStorage) || - compare(sopClassUID, UID_RTDoseStorage) || +// compare(sopClassUID, UID_ParametricMapStorage) || + compare(sopClassUID, UID_PhotoacousticImageStorage) || +// compare(sopClassUID, UID_RTDoseStorage) || compare(sopClassUID, UID_RTImageStorage) || compare(sopClassUID, UID_UltrasoundMultiframeImageStorage) || compare(sopClassUID, UID_VideoEndoscopicImageStorage) || @@ -2379,7 +2393,7 @@ OFCondition DicomDirInterface::checkMandatoryAttributes(DcmMetaInfo *metainfo, metainfo->findAndGetOFStringArray(DCM_TransferSyntaxUID, transferSyntax); metainfo->findAndGetOFStringArray(DCM_MediaStorageSOPClassUID, mediaSOPClassUID); E_DirRecType recordType = sopClassToRecordType(mediaSOPClassUID); - /* hanging protocol, palette and implant files are handled separately */ + /* some directory record types are handled separately */ if (recordType == ERT_HangingProtocol) { /* check whether all type 1 elements are really present */ @@ -2564,6 +2578,7 @@ OFCondition DicomDirInterface::checkMandatoryAttributes(DcmMetaInfo *metainfo, } break; case ERT_Presentation: + case ERT_WfPresentation: if (!checkExistsWithValue(dataset, DCM_InstanceNumber, filename)) result = EC_MissingAttribute; if (!checkExistsWithValue(dataset, DCM_ContentLabel, filename)) @@ -2950,6 +2965,7 @@ OFBool DicomDirInterface::recordMatchesDataset(DcmDirectoryRecord *record, case ERT_Radiotherapy: case ERT_Annotation: case ERT_Inventory: + case ERT_WfPresentation: /* The attribute ReferencedSOPInstanceUID is automatically * put into a Directory Record when a filename is present. */ @@ -3335,6 +3351,42 @@ DcmDirectoryRecord *DicomDirInterface::buildPresentationRecord(DcmDirectoryRecor } +// create or update waveform presentation state record and copy required values from dataset +DcmDirectoryRecord *DicomDirInterface::buildWfPresentationRecord(DcmDirectoryRecord *record, + DcmFileFormat *fileformat, + const OFString &referencedFileID, + const OFFilename &sourceFilename) +{ + /* create new waveform presentation record */ + if (record == NULL) + record = new DcmDirectoryRecord(ERT_WfPresentation, referencedFileID.c_str(), sourceFilename, fileformat); + if (record != NULL) + { + /* check whether new record is ok */ + if (record->error().good()) + { + DcmDataset *dataset = fileformat->getDataset(); + /* copy attribute values from dataset to waveform presentation record */ + copyElementType1(dataset, DCM_InstanceNumber, record, sourceFilename); + copyElementType1(dataset, DCM_ContentLabel, record, sourceFilename); + copyElementType2(dataset, DCM_ContentDescription, record, sourceFilename); + copyElementType1(dataset, DCM_PresentationCreationDate, record, sourceFilename); + copyElementType1(dataset, DCM_PresentationCreationTime, record, sourceFilename); + copyElementType3(dataset, DCM_ContentCreatorName, record, sourceFilename); + copyElementType1C(dataset, DCM_ReferencedSeriesSequence, record, sourceFilename); + // tbd: need to check content of the referenced series sequence + } else { + printRecordErrorMessage(record->error(), ERT_WfPresentation, "create"); + /* free memory */ + delete record; + record = NULL; + } + } else + printRecordErrorMessage(EC_MemoryExhausted, ERT_WfPresentation, "create"); + return record; +} + + // create or update waveform record and copy required values from dataset DcmDirectoryRecord *DicomDirInterface::buildWaveformRecord(DcmDirectoryRecord *record, DcmFileFormat *fileformat, @@ -4715,6 +4767,9 @@ DcmDirectoryRecord *DicomDirInterface::addRecord(DcmDirectoryRecord *parent, case ERT_Inventory: record = buildInventoryRecord(record, fileformat, referencedFileID, sourceFilename); break; + case ERT_WfPresentation: + record = buildWfPresentationRecord(record, fileformat, referencedFileID, sourceFilename); + break; default: /* it can only be an image */ record = buildImageRecord(record, fileformat, referencedFileID, sourceFilename); @@ -4879,6 +4934,7 @@ void DicomDirInterface::inventMissingInstanceLevelAttributes(DcmDirectoryRecord case ERT_Assessment: case ERT_Radiotherapy: case ERT_Annotation: + case ERT_WfPresentation: if (!record->tagExistsWithValue(DCM_InstanceNumber)) setDefaultValue(record, DCM_InstanceNumber, AutoInstanceNumber++); break; diff --git a/dcmdata/libsrc/dcdict.cc b/dcmdata/libsrc/dcdict.cc index 0390c911..a9b80f1a 100644 --- a/dcmdata/libsrc/dcdict.cc +++ b/dcmdata/libsrc/dcdict.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -232,7 +232,7 @@ splitFields(const char* line, char* fields[], int maxFields, char splitChar) size_t len; do { -#ifdef __BORLANDC__ +#ifdef HAVE_CLASSIC_BORLAND_COMPILER // Borland Builder expects a non-const argument p = strchr(OFconst_cast(char *, line), splitChar); #else diff --git a/dcmdata/libsrc/dcdictbi.cc b/dcmdata/libsrc/dcdictbi.cc index 2bf239d6..def1a9ca 100644 --- a/dcmdata/libsrc/dcdictbi.cc +++ b/dcmdata/libsrc/dcdictbi.cc @@ -4,7 +4,7 @@ ** ** User: joergr ** Host: thinkpad2 -** Date: 2024-11-16 10:42:05 +** Date: 2025-11-21 11:54:35 ** Prog: /home/joergr/Source/dcmtk-full/public/dcmdata/libsrc/mkdictbi ** ** From: ../data/dicom.dic @@ -1244,6 +1244,22 @@ static const DBI_SimpleEntry simpleBuiltinDict[] = { EVR_SQ, "RelatedSeriesSequence", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL } + , { 0x0008, 0x1301, 0x0008, 0x1301, + EVR_SQ, "PrincipalDiagnosisCodeSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0008, 0x1302, 0x0008, 0x1302, + EVR_SQ, "PrimaryDiagnosisCodeSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0008, 0x1303, 0x0008, 0x1303, + EVR_SQ, "SecondaryDiagnosesCodeSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0008, 0x1304, 0x0008, 0x1304, + EVR_SQ, "HistologicalDiagnosesCodeSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } , { 0x0008, 0x2110, 0x0008, 0x2110, EVR_CS, "RETIRED_LossyImageCompressionRetired", 1, 1, "DICOM/retired", DcmDictRange_Unspecified, DcmDictRange_Unspecified, @@ -2290,6 +2306,30 @@ static const DBI_SimpleEntry simpleBuiltinDict[] = { EVR_PN, "PatientName", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL } + , { 0x0010, 0x0011, 0x0010, 0x0011, + EVR_SQ, "PersonNamesToUseSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0010, 0x0012, 0x0010, 0x0012, + EVR_LT, "NameToUse", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0010, 0x0013, 0x0010, 0x0013, + EVR_UT, "NameToUseComment", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0010, 0x0014, 0x0010, 0x0014, + EVR_SQ, "ThirdPersonPronounsSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0010, 0x0015, 0x0010, 0x0015, + EVR_SQ, "PronounCodeSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0010, 0x0016, 0x0010, 0x0016, + EVR_UT, "PronounComment", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } , { 0x0010, 0x0020, 0x0010, 0x0020, EVR_LO, "PatientID", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, @@ -2342,6 +2382,34 @@ static const DBI_SimpleEntry simpleBuiltinDict[] = { EVR_CS, "PatientSex", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL } + , { 0x0010, 0x0041, 0x0010, 0x0041, + EVR_SQ, "GenderIdentitySequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0010, 0x0042, 0x0010, 0x0042, + EVR_UT, "SexParametersForClinicalUseCategoryComment", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0010, 0x0043, 0x0010, 0x0043, + EVR_SQ, "SexParametersForClinicalUseCategorySequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0010, 0x0044, 0x0010, 0x0044, + EVR_SQ, "GenderIdentityCodeSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0010, 0x0045, 0x0010, 0x0045, + EVR_UT, "GenderIdentityComment", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0010, 0x0046, 0x0010, 0x0046, + EVR_SQ, "SexParametersForClinicalUseCategoryCodeSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0010, 0x0047, 0x0010, 0x0047, + EVR_UR, "SexParametersForClinicalUseCategoryReference", 1, -1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } , { 0x0010, 0x0050, 0x0010, 0x0050, EVR_SQ, "PatientInsurancePlanCodeSequence", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, @@ -2507,13 +2575,17 @@ static const DBI_SimpleEntry simpleBuiltinDict[] = { DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL } , { 0x0010, 0x2160, 0x0010, 0x2160, - EVR_SH, "EthnicGroup", 1, 1, "DICOM", + EVR_SH, "RETIRED_EthnicGroup", 1, 1, "DICOM/retired", DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL } , { 0x0010, 0x2161, 0x0010, 0x2161, EVR_SQ, "EthnicGroupCodeSequence", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL } + , { 0x0010, 0x2162, 0x0010, 0x2162, + EVR_UC, "EthnicGroups", 1, -1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } , { 0x0010, 0x2180, 0x0010, 0x2180, EVR_SH, "Occupation", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, @@ -3950,6 +4022,54 @@ static const DBI_SimpleEntry simpleBuiltinDict[] = { EVR_LO, "ImageQualityIndicatorSize", 1, -1, "DICOM/DICONDE", DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL } + , { 0x0014, 0x4101, 0x0014, 0x4101, + EVR_SQ, "WaveDimensionsDefinitionSequence", 1, 1, "DICOM/DICONDE", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0014, 0x4102, 0x0014, 0x4102, + EVR_US, "WaveDimensionNumber", 1, 1, "DICOM/DICONDE", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0014, 0x4103, 0x0014, 0x4103, + EVR_LO, "WaveDimensionDescription", 1, 1, "DICOM/DICONDE", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0014, 0x4104, 0x0014, 0x4104, + EVR_US, "WaveDimensionUnit", 1, 1, "DICOM/DICONDE", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0014, 0x4105, 0x0014, 0x4105, + EVR_CS, "WaveDimensionValueType", 1, 1, "DICOM/DICONDE", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0014, 0x4106, 0x0014, 0x4106, + EVR_SQ, "WaveDimensionValuesSequence", 1, -1, "DICOM/DICONDE", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0014, 0x4107, 0x0014, 0x4107, + EVR_US, "ReferencedWaveDimension", 1, 1, "DICOM/DICONDE", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0014, 0x4108, 0x0014, 0x4108, + EVR_SL, "IntegerNumericValue", 1, 1, "DICOM/DICONDE", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0014, 0x4109, 0x0014, 0x4109, + EVR_OB, "ByteNumericValue", 1, 1, "DICOM/DICONDE", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0014, 0x410a, 0x0014, 0x410a, + EVR_OW, "ShortNumericValue", 1, 1, "DICOM/DICONDE", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0014, 0x410b, 0x0014, 0x410b, + EVR_OF, "SinglePrecisionFloatingPointNumericValue", 1, 1, "DICOM/DICONDE", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0014, 0x410c, 0x0014, 0x410c, + EVR_OD, "DoublePrecisionFloatingPointNumericValue", 1, 1, "DICOM/DICONDE", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } , { 0x0014, 0x5002, 0x0014, 0x5002, EVR_IS, "LINACEnergy", 1, 1, "DICOM/DICONDE", DcmDictRange_Unspecified, DcmDictRange_Unspecified, @@ -20394,6 +20514,14 @@ static const DBI_SimpleEntry simpleBuiltinDict[] = { EVR_DT, "ObservationStartDateTime", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL } + , { 0x0040, 0xa034, 0x0040, 0xa034, + EVR_DT, "EffectiveStartDateTime", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xa035, 0x0040, 0xa035, + EVR_DT, "EffectiveStopDateTime", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } , { 0x0040, 0xa040, 0x0040, 0xa040, EVR_CS, "ValueType", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, @@ -20814,6 +20942,82 @@ static const DBI_SimpleEntry simpleBuiltinDict[] = { EVR_SQ, "WaveformAnnotationSequence", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL } + , { 0x0040, 0xb030, 0x0040, 0xb030, + EVR_SQ, "StructuredWaveformAnnotationSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb031, 0x0040, 0xb031, + EVR_SQ, "WaveformAnnotationDisplaySelectionSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb032, 0x0040, 0xb032, + EVR_US, "ReferencedMontageIndex", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb033, 0x0040, 0xb033, + EVR_SQ, "WaveformTextualAnnotationSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb034, 0x0040, 0xb034, + EVR_DT, "AnnotationDateTime", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb035, 0x0040, 0xb035, + EVR_SQ, "DisplayedWaveformSegmentSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb036, 0x0040, 0xb036, + EVR_DT, "SegmentDefinitionDateTime", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb037, 0x0040, 0xb037, + EVR_SQ, "MontageActivationSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb038, 0x0040, 0xb038, + EVR_DS, "MontageActivationTimeOffset", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb039, 0x0040, 0xb039, + EVR_SQ, "WaveformMontageSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb03a, 0x0040, 0xb03a, + EVR_IS, "ReferencedMontageChannelNumber", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb03b, 0x0040, 0xb03b, + EVR_LT, "MontageName", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb03c, 0x0040, 0xb03c, + EVR_SQ, "MontageChannelSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb03d, 0x0040, 0xb03d, + EVR_US, "MontageIndex", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb03e, 0x0040, 0xb03e, + EVR_IS, "MontageChannelNumber", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb03f, 0x0040, 0xb03f, + EVR_LO, "MontageChannelLabel", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb040, 0x0040, 0xb040, + EVR_SQ, "MontageChannelSourceCodeSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb041, 0x0040, 0xb041, + EVR_SQ, "ContributingChannelSourcesSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x0040, 0xb042, 0x0040, 0xb042, + EVR_FL, "ChannelWeight", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } , { 0x0040, 0xdb00, 0x0040, 0xdb00, EVR_CS, "TemplateIdentifier", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, @@ -21690,6 +21894,10 @@ static const DBI_SimpleEntry simpleBuiltinDict[] = { EVR_SQ, "OrganizationalRoleCodeSequence", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL } + , { 0x0044, 0x0110, 0x0044, 0x0110, + EVR_SQ, "RTAssertionsSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } #ifdef ENABLE_PRIVATE_TAGS , { 0x0045, 0x0004, 0x0045, 0x0004, EVR_CS, "AES", 1, 1, "PrivateTag", @@ -22546,6 +22754,10 @@ static const DBI_SimpleEntry simpleBuiltinDict[] = { EVR_UL, "TotalPixelMatrixFocalPlanes", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL } + , { 0x0048, 0x0304, 0x0048, 0x0304, + EVR_CS, "TilesOverlap", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } , { 0x0050, 0x0004, 0x0050, 0x0004, EVR_CS, "CalibrationImage", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, @@ -27432,6 +27644,26 @@ static const DBI_SimpleEntry simpleBuiltinDict[] = { EVR_DS, "DVHMeanDose", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL } + , { 0x3004, 0x0080, 0x3004, 0x0080, + EVR_SQ, "DoseCalculationModelSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x3004, 0x0081, 0x3004, 0x0081, + EVR_SQ, "DoseCalculationAlgorithmSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x3004, 0x0082, 0x3004, 0x0082, + EVR_CS, "CommissioningStatus", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x3004, 0x0083, 0x3004, 0x0083, + EVR_SQ, "DoseCalculationModelParameterSequence", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x3004, 0x0084, 0x3004, 0x0084, + EVR_CS, "DoseDepositionCalculationMedium", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } , { 0x3006, 0x0002, 0x3006, 0x0002, EVR_SH, "StructureSetLabel", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, @@ -29528,6 +29760,14 @@ static const DBI_SimpleEntry simpleBuiltinDict[] = { EVR_IS, "NumberOfPaintings", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL } + , { 0x300a, 0x039b, 0x300a, 0x039b, + EVR_FL, "ScanSpotGantryAngles", 1, -1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } + , { 0x300a, 0x039c, 0x300a, 0x039c, + EVR_FL, "ScanSpotPatientSupportAngles", 1, -1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, + NULL } , { 0x300a, 0x03a0, 0x300a, 0x03a0, EVR_SQ, "IonToleranceTableSequence", 1, 1, "DICOM", DcmDictRange_Unspecified, DcmDictRange_Unspecified, diff --git a/dcmdata/libsrc/dcdirrec.cc b/dcmdata/libsrc/dcdirrec.cc index c374c347..43af8939 100644 --- a/dcmdata/libsrc/dcdirrec.cc +++ b/dcmdata/libsrc/dcdirrec.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -89,7 +89,8 @@ static const char *DRTypeNames[] = "ASSESSMENT", "RADIOTHERAPY", "ANNOTATION", - "INVENTORY" + "INVENTORY", + "WF PRESENTATION" }; static const short DIM_OF_DRTypeNames = OFstatic_cast(short, (sizeof(DRTypeNames) / sizeof(DRTypeNames[0]))); @@ -425,6 +426,7 @@ OFCondition DcmDirectoryRecord::checkHierarchy(const E_DirRecType upperRecord, case ERT_Assessment: case ERT_Radiotherapy: case ERT_Annotation: + case ERT_WfPresentation: case ERT_Private: l_error = EC_Normal; break; @@ -524,6 +526,7 @@ OFCondition DcmDirectoryRecord::checkHierarchy(const E_DirRecType upperRecord, case ERT_Radiotherapy: case ERT_Annotation: case ERT_Inventory: + case ERT_WfPresentation: case ERT_Private: switch (lowerRecord) { diff --git a/dcmdata/libsrc/dcdocdec.cc b/dcmdata/libsrc/dcdocdec.cc new file mode 100644 index 00000000..10c9d6d7 --- /dev/null +++ b/dcmdata/libsrc/dcdocdec.cc @@ -0,0 +1,245 @@ +/* + * + * Copyright (C) 2007-2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmdata + * + * Author: Marco Eichelberg, Tingyan Xu + * + * Purpose: Helper class for extracting encapsulated file from a DICOM encapsulated storage object + * + */ + +//make sure OS specific configuration is included first +#include "dcmtk/config/osconfig.h" +#include "dcmtk/dcmdata/dcdocdec.h" +#include "dcmtk/dcmdata/dcdeftag.h" +#include "dcmtk/dcmdata/dcuid.h" + +BEGIN_EXTERN_C +#include /* for O_BINARY */ +#ifdef HAVE_IO_H +#include /* for setmode() on Windows */ +#endif +END_EXTERN_C + + +DcmDocumentDecapsulator::DcmDocumentDecapsulator() +: readMode_(ERM_autoDetect) +, inputXfer_(EXS_Unknown) +, execString_(NULL) +, inputFname_(NULL) +, outputFname_(NULL) +, dicomFile_() +{ +} + + +DcmDocumentDecapsulator::~DcmDocumentDecapsulator() +{ +} + + +OFCondition DcmDocumentDecapsulator::loadDICOMFile() +{ + // check filename + if ((inputFname_ == NULL) || (strlen(inputFname_) == 0)) + { + DCMDATA_ERROR("invalid input filename: "); + return EC_InvalidFilename; + } + + // read DICOM file + DCMDATA_INFO("open input file " << inputFname_); + OFCondition cond = dicomFile_.loadFile(inputFname_, inputXfer_, EGL_noChange, DCM_MaxReadLength, readMode_); + if (cond.bad()) + { + DCMDATA_ERROR(cond.text() << ": reading file: " << inputFname_); + return cond; + } + + return EC_Normal; +} + + +OFCondition DcmDocumentDecapsulator::writeEncapsulatedContentToFile() +{ + // check filename + if ((outputFname_ == NULL) || (strlen(outputFname_) == 0)) + { + DCMDATA_ERROR("invalid output filename: "); + return EC_InvalidFilename; + } + + // get encapsulated document + DcmDataset* dataset = dicomFile_.getDataset(); + DcmElement *delem = NULL; + OFCondition cond = dataset->findAndGetElement(DCM_EncapsulatedDocument, delem); + if (cond.bad() || delem == NULL) + { + DCMDATA_ERROR("encapsulated document missing"); + return EC_MissingAttribute; + } + + Uint32 len = delem->getLength(); + Uint8 *encapDocment = NULL; + cond = delem->getUint8Array(encapDocment); + if (cond.bad() || encapDocment == NULL || len == 0) + { + DCMDATA_ERROR("encapsulated Document empty or wrong VR"); + return EC_MissingAttribute; + } + + // get and check element encapsulated document length + Uint32 lenElem; + cond = dataset->findAndGetUint32(DCM_EncapsulatedDocumentLength, lenElem); + + // EncapsulatedDocumentLength Element is invalid or + // it does not fit the length of the encapsulated document + // (it has to be equal or equal to EncapsulatedDocumentLength -1) + if (cond.bad() || (lenElem != len && lenElem != len - 1)) + { + DCMDATA_DEBUG("EncapsulatedDocumentLength missing or invalid, using length of EncapsulatedDocument"); + lenElem = len; + + // try to retrieve the SOP Class UID, ignore errors + OFString sopClass; + (void) dataset->findAndGetOFString(DCM_SOPClassUID, sopClass); + + // try to determine if we need to strip a pad byte at the end of the element + if (sopClass == UID_EncapsulatedPDFStorage) + { + // The PDF format expects files to end with %%EOF followed by CR/LF + // (although in some cases the CR/LF may be missing or you might only find CR or LF). + // If the last character of the file is not a CR or LF, and not the + // letter 'F', we assume it is either trailing garbage or a pad byte, and remove it. + if (encapDocment[lenElem-1] != 10 && encapDocment[lenElem-1] != 13 && encapDocment[lenElem-1] != 'F') + { + DCMDATA_DEBUG("removing pad byte at end of encapsulated document"); + --lenElem; + } + } + else if (sopClass == UID_EncapsulatedCDAStorage) + { + // CDA documents end with a closing XML tag, optionally followed by whitespace. + // If the last character of the file is not a CR ('\r', 13) or LF ('\n', 10), and not the + // letter '>', we assume it is either trailing garbage or a pad byte, and remove it. + if (encapDocment[lenElem - 1] != '\n' && encapDocment[lenElem - 1] != '\r' && encapDocment[lenElem - 1] != '>') + { + DCMDATA_DEBUG("removing pad byte at end of encapsulated document"); + --lenElem; + } + } + else if (sopClass == UID_EncapsulatedSTLStorage || sopClass == UID_EncapsulatedOBJStorage || sopClass == UID_EncapsulatedMTLStorage) + { + // STL, OBJ and MTL are text formats. We remove a trailing null byte, but nothing else. + if (encapDocment[lenElem - 1] == '\0') + { + DCMDATA_DEBUG("removing pad byte at end of encapsulated document"); + --lenElem; + } + } + else + { + DCMDATA_DEBUG("Unknown encapsulated document SOP class, not removing any pad byte"); + } + } + + DCMDATA_INFO( "writing encapsulated document to " << outputFname_); + + if (strcmp(outputFname_, "-") == 0) + { +#ifdef _WIN32 + // Set "stdout" to binary mode + if (setmode(fileno(stdout), O_BINARY) == -1) + { + DCMDATA_ERROR("Failed to switch stdout to binary mode"); + return makeOFCondition(OFM_dcmdata, 19, OF_error, "file write error"); + } +#endif + + // write encapsulated document to stdout + if (lenElem != fwrite(encapDocment, 1, lenElem, stdout)) + { + DCMDATA_ERROR("write error to stdout"); + return makeOFCondition(OFM_dcmdata, 19, OF_error, "file write error"); + } + } + else + { + // write encapsulated document to file + OFFile encapfile; + if (!encapfile.fopen(outputFname_, "wb")) + { + DCMDATA_ERROR("unable to create file " << outputFname_); + return makeOFCondition(OFM_dcmdata, 19, OF_error, "file open error"); + } + + if (lenElem != encapfile.fwrite(encapDocment, 1, lenElem)) + { + DCMDATA_ERROR("write error in file " << outputFname_); + (void) encapfile.fclose(); + return makeOFCondition(OFM_dcmdata, 19, OF_error, "file write error"); + } + + if (encapfile.fclose()) + { + DCMDATA_ERROR("write error in file " << outputFname_); + return makeOFCondition(OFM_dcmdata, 19, OF_error, "file close error"); + } + } + + DCMDATA_INFO( "conversion successful"); + return EC_Normal; +} + + +/** Replace all occurrences of pattern in srcstr with substitute and return + * the result as a new OFString variable. + * @param srcstr source string + * @param pattern pattern string to be substituted. + * @param substitute substitute for occurences of pattern in srcstr + * @return string with patterns replaced + */ +static OFString replaceChars(const OFString &srcstr, const OFString &pattern, const OFString &substitute) +{ + OFString result = srcstr; + size_t pos = 0; + + while (pos != OFString_npos) + { + pos = result.find(pattern, pos); + + if (pos != OFString_npos) + { + result.replace(pos, pattern.size(), substitute); + pos += substitute.size(); + } + } + + return result; +} + + +#define FILENAME_PLACEHOLDER "#f" + +OFCondition DcmDocumentDecapsulator::executeCommand() +{ + if (execString_) + { + OFString cmdStr = replaceChars(execString_, OFString(FILENAME_PLACEHOLDER), outputFname_); + + // Execute command and return result + if (system(cmdStr.c_str()) != 0) return EC_CommandLineFailed; + } + return EC_Normal; +} diff --git a/dcmdata/libsrc/dcelem.cc b/dcmdata/libsrc/dcelem.cc index 3b9cc2bf..e5bfc57c 100644 --- a/dcmdata/libsrc/dcelem.cc +++ b/dcmdata/libsrc/dcelem.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -74,44 +74,21 @@ DcmElement::DcmElement(const DcmElement &elem) // is added to the Length for this purpose. if (getLengthField() & 1) { -#ifdef HAVE_STD__NOTHROW // we want to use a non-throwing new here if available // If the allocation fails, we report an EC_MemoryExhausted error // back to the caller. fValue = new (std::nothrow) Uint8[getLengthField() + 1 + pad]; // protocol error: odd value length -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - fValue = new Uint8[getLengthField() + 1 + pad]; // protocol error: odd value length - } - catch (STD_NAMESPACE bad_alloc const &) - { - fValue = NULL; - } -#endif + if (fValue) fValue[getLengthField()] = 0; setLengthField(getLengthField() + 1); // make Length even } else { -#ifdef HAVE_STD__NOTHROW // we want to use a non-throwing new here if available. // If the allocation fails, we report an EC_MemoryExhausted error // back to the caller. fValue = new (std::nothrow) Uint8[getLengthField() + pad]; -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - fValue = new Uint8[getLengthField() + pad]; - } - catch (STD_NAMESPACE bad_alloc const &) - { - fValue = NULL; - } -#endif } if (!fValue) @@ -133,13 +110,9 @@ DcmElement &DcmElement::operator=(const DcmElement &obj) { if (this != &obj) { -#if defined(HAVE_STD__NOTHROW) && defined(HAVE_NOTHROW_DELETE) // if created with the nothrow version it must also be deleted with // the nothrow version else memory error. operator delete[] (fValue, std::nothrow); -#else - delete[] fValue; -#endif delete fLoadValue; fLoadValue = NULL; fValue = NULL; @@ -159,44 +132,20 @@ DcmElement &DcmElement::operator=(const DcmElement &obj) if (getLengthField() & 1) { -#ifdef HAVE_STD__NOTHROW // we want to use a non-throwing new here if available. // If the allocation fails, we report an EC_MemoryExhausted error // back to the caller. fValue = new (std::nothrow) Uint8[getLengthField() + 1 + pad]; // protocol error: odd value length -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - fValue = new Uint8[getLengthField() + 1 + pad]; // protocol error: odd value length - } - catch (STD_NAMESPACE bad_alloc const &) - { - fValue = NULL; - } -#endif if (fValue) fValue[getLengthField()] = 0; setLengthField(getLengthField() + 1); // make Length even } else { -#ifdef HAVE_STD__NOTHROW // we want to use a non-throwing new here if available. // If the allocation fails, we report an EC_MemoryExhausted error // back to the caller. fValue = new (std::nothrow) Uint8[getLengthField() + pad]; -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - fValue = new Uint8[getLengthField() + pad]; - } - catch (STD_NAMESPACE bad_alloc const &) - { - fValue = NULL; - } -#endif } if (!fValue) @@ -256,13 +205,9 @@ OFCondition DcmElement::copyFrom(const DcmObject& rhs) DcmElement::~DcmElement() { -#if defined(HAVE_STD__NOTHROW) && defined(HAVE_NOTHROW_DELETE) // if created with the nothrow version it must also be deleted with // the nothrow version else memory error. operator delete[] (fValue, std::nothrow); -#else - delete[] fValue; -#endif delete fLoadValue; } @@ -273,13 +218,9 @@ DcmElement::~DcmElement() OFCondition DcmElement::clear() { errorFlag = EC_Normal; -#if defined(HAVE_STD__NOTHROW) && defined(HAVE_NOTHROW_DELETE) // if created with the nothrow version it must also be deleted with // the nothrow version else memory error. operator delete[] (fValue, std::nothrow); -#else - delete[] fValue; -#endif fValue = NULL; delete fLoadValue; fLoadValue = NULL; @@ -358,20 +299,9 @@ OFCondition DcmElement::detachValueField(OFBool copy) if (l_error.good()) { Uint8 * newValue; -#ifdef HAVE_STD__NOTHROW // we want to use a non-throwing new here if available newValue = new (std::nothrow) Uint8[getLengthField()]; -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - newValue = new Uint8[getLengthField()]; - } - catch (STD_NAMESPACE bad_alloc const &) - { - newValue = NULL; - } -#endif + if (newValue) { memcpy(newValue, fValue, size_t(getLengthField())); @@ -752,22 +682,12 @@ Uint8 *DcmElement::newValueField() return NULL; } /* create an array of Length+1 bytes */ -#ifdef HAVE_STD__NOTHROW + // we want to use a non-throwing new here if available. // If the allocation fails, we report an EC_MemoryExhausted error // back to the caller. value = new (std::nothrow) Uint8[lengthField + 1]; // protocol error: odd value length -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - value = new Uint8[lengthField + 1]; // protocol error: odd value length - } - catch (STD_NAMESPACE bad_alloc const &) - { - value = NULL; - } -#endif + /* if creation was successful, set last byte to 0 (in order to initialize this byte) */ /* (no value will be assigned to this byte later, since Length was odd) */ if (value) @@ -781,22 +701,13 @@ Uint8 *DcmElement::newValueField() } /* if this element's length is even, create a corresponding array of Length bytes */ else -#ifdef HAVE_STD__NOTHROW + { // we want to use a non-throwing new here if available. // If the allocation fails, we report an EC_MemoryExhausted error // back to the caller. value = new (std::nothrow) Uint8[lengthField]; -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - value = new Uint8[lengthField]; - } - catch (STD_NAMESPACE bad_alloc const &) - { - value = NULL; - } -#endif + } + /* if creation was not successful set member error flag correspondingly */ if (!value) errorFlag = EC_MemoryExhausted; @@ -845,22 +756,12 @@ OFCondition DcmElement::changeValue(const void *value, { Uint8 * newValue; // allocate new memory for value -#ifdef HAVE_STD__NOTHROW + // we want to use a non-throwing new here if available. // If the allocation fails, we report an EC_MemoryExhausted error // back to the caller. newValue = new (std::nothrow) Uint8[getLengthField() + num]; -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - newValue = new Uint8[getLengthField() + num]; - } - catch (STD_NAMESPACE bad_alloc const &) - { - newValue = NULL; - } -#endif + if (!newValue) errorFlag = EC_MemoryExhausted; if (errorFlag.good()) @@ -873,13 +774,9 @@ OFCondition DcmElement::changeValue(const void *value, memcpy(newValue, fValue, size_t(getLengthField())); // copy value passed as a parameter to the end memcpy(&newValue[getLengthField()], OFstatic_cast(const Uint8 *, value), size_t(num)); -#if defined(HAVE_STD__NOTHROW) && defined(HAVE_NOTHROW_DELETE) // if created with the nothrow version it must also be deleted with // the nothrow version else memory error. operator delete[] (fValue, std::nothrow); -#else - delete[] fValue; -#endif fValue = newValue; setLengthField(getLengthField() + num); } else @@ -969,6 +866,21 @@ OFCondition DcmElement::putFloat32(const Float32 /*val*/, } +OFCondition DcmElement::putSint64(const Sint64 /*val*/, + const unsigned long /*pos*/) +{ + errorFlag = EC_IllegalCall; + return errorFlag; +} + + +OFCondition DcmElement::putUint64(const Uint64 /*val*/, + const unsigned long /*pos*/) +{ + errorFlag = EC_IllegalCall; + return errorFlag; +} + OFCondition DcmElement::putFloat64(const Float64 /*val*/, const unsigned long /*pos*/) { @@ -1033,6 +945,21 @@ OFCondition DcmElement::putFloat32Array(const Float32 * /*val*/, } +OFCondition DcmElement::putSint64Array(const Sint64 * /*val*/, + const unsigned long /*num*/) +{ + errorFlag = EC_IllegalCall; + return errorFlag; +} + + +OFCondition DcmElement::putUint64Array(const Uint64 * /*val*/, + const unsigned long /*num*/) +{ + errorFlag = EC_IllegalCall; + return errorFlag; +} + OFCondition DcmElement::putFloat64Array(const Float64 * /*val*/, const unsigned long /*num*/) { @@ -1048,13 +975,9 @@ OFCondition DcmElement::putValue(const void * newValue, if (fValue) { -#if defined(HAVE_STD__NOTHROW) && defined(HAVE_NOTHROW_DELETE) // if created with the nothrow version it must also be deleted with // the nothrow version else memory error. operator delete[] (fValue, std::nothrow); -#else - delete[] fValue; -#endif } fValue = NULL; @@ -1111,13 +1034,9 @@ OFCondition DcmElement::createEmptyValue(const Uint32 length) errorFlag = EC_Normal; if (fValue) { -#if defined(HAVE_STD__NOTHROW) && defined(HAVE_NOTHROW_DELETE) // if created with the nothrow version it must also be deleted with // the nothrow version else memory error. operator delete[] (fValue, std::nothrow); -#else - delete[] fValue; -#endif } fValue = NULL; if (fLoadValue) @@ -1213,8 +1132,16 @@ OFCondition DcmElement::read(DcmInputStream &inStream, if (fLoadValue) { - offile_off_t skipped = inStream.skip(getLengthField()); - if (skipped < OFstatic_cast(offile_off_t, getLengthField())) + /* enforce old (pre DCMTK 3.5.2) behaviour ? */ + Uint32 lengthField = getLengthField(); + if ((lengthField & 1) && !dcmAcceptOddAttributeLength.get()) + { + lengthField++; + setLengthField(lengthField); // make Length even + } + + offile_off_t skipped = inStream.skip(lengthField); + if (skipped < OFstatic_cast(offile_off_t, lengthField)) { /* If desired, specific parser errors will be ignored */ if (dcmIgnoreParsingErrors.get()) @@ -1229,13 +1156,9 @@ OFCondition DcmElement::read(DcmInputStream &inStream, } } /* if there is already a value for this element, delete this value */ -#if defined(HAVE_STD__NOTHROW) && defined(HAVE_NOTHROW_DELETE) // if created with the nothrow version it must also be deleted with // the nothrow version else memory error. operator delete[] (fValue, std::nothrow); -#else - delete[] fValue; -#endif /* set the transfer state to ERW_inWork */ setTransferState(ERW_inWork); } @@ -1695,19 +1618,23 @@ void DcmElement::writeJsonCloser(STD_NAMESPACE ostream &out, OFCondition DcmElement::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { + OFCondition result = EC_Normal; + /* always write JSON Opener */ writeJsonOpener(out, format); + /* write element value (if non-empty) */ if (!isEmpty()) { - OFString value; - if (format.asBulkDataURI(getTag(), value)) + if (format.asBulkDataURI(getTag(), getLength())) { - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, value); + /* adjust byte order to little endian */ + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); + result = format.writeBulkData(out, getTag(), getLengthField(), byteValues); } else { + OFString value; OFCondition status = getOFString(value, 0L); if (status.bad()) return status; @@ -1725,10 +1652,10 @@ OFCondition DcmElement::writeJson(STD_NAMESPACE ostream &out, format.printValueSuffix(out); } } + /* write JSON Closer */ writeJsonCloser(out, format); - /* always report success */ - return EC_Normal; + return result; } @@ -1982,13 +1909,9 @@ OFCondition DcmElement::createValueFromTempFile(DcmInputStreamFactory *factory, { if (factory && !(length & 1)) { -#if defined(HAVE_STD__NOTHROW) && defined(HAVE_NOTHROW_DELETE) // if created with the nothrow version it must also be deleted with // the nothrow version else memory error. operator delete[] (fValue, std::nothrow); -#else - delete[] fValue; -#endif fValue = 0; delete fLoadValue; fLoadValue = factory; @@ -2000,6 +1923,17 @@ OFCondition DcmElement::createValueFromTempFile(DcmInputStreamFactory *factory, } +Uint16 DcmElement::decodedBitsAllocated( + Uint16 bitsAllocated, + Uint16 bitsStored) const +{ + // Note: This method only handles the uncompressed case. + // A different implementation is provided in class DcmPixelData. + if (bitsStored > bitsAllocated) return 0; + return bitsAllocated; +} + + // the following macro makes the source code more readable and easier to maintain #define GET_AND_CHECK_UINT16_VALUE(tag, variable) \ @@ -2029,6 +1963,7 @@ OFCondition DcmElement::getUncompressedFrameSize(DcmItem *dataset, Uint16 cols = 0; Uint16 samplesPerPixel = 0; Uint16 bitsAllocated = 0; + Uint16 bitsStored = 0; Sint32 numberOfFrames = 1; OFString photometricInterpretation; @@ -2091,7 +2026,15 @@ OFCondition DcmElement::getUncompressedFrameSize(DcmItem *dataset, /* see PS3.3 Table C.7-11c: "Bits Allocated (0028,0100) shall be either 1, or a multiple of 8." */ else if ((bitsAllocated == 0) || ((bitsAllocated > 1) && (bitsAllocated % 8 != 0))) DCMDATA_WARN("DcmElement: Dubious value (" << bitsAllocated << ") for element BitsAllocated " << DCM_BitsAllocated); + + GET_AND_CHECK_UINT16_VALUE(DCM_BitsStored, bitsStored) + else if (bitsStored > bitsAllocated) + { + DCMDATA_WARN("DcmElement: Dubious value (" << bitsStored << ") for element BitsStored " << DCM_BitsAllocated << " larger than value of BitsAllocated " << DCM_BitsAllocated); + result = EC_InvalidValue; + } } + /* if all checks were passed... */ if (result.good()) { @@ -2129,26 +2072,42 @@ OFCondition DcmElement::getUncompressedFrameSize(DcmItem *dataset, DCMDATA_WARN("DcmElement: failed to compute size of PixelData element"); } - /* compute frame size (TODO: check for 32-bit integer overflow?) */ - if ((bitsAllocated % 8) == 0) + // Determine the effective value for Bits Allocated, taking into account compression + Uint16 effectiveBitsAllocated = decodedBitsAllocated(bitsAllocated, bitsStored); + if (effectiveBitsAllocated == 0) { - const Uint16 bytesAllocated = bitsAllocated / 8; - frameSize = bytesAllocated * rows * cols * samplesPerPixel; + DCMDATA_WARN("DcmElement: Encapsulated image with BitsAllocated=" << bitsAllocated << " and BitsStored=" << bitsStored << " cannot be decoded"); + result = EC_InvalidValue; } else { - /* need to split calculation in order to avoid integer overflow for large pixel data */ - const Uint32 v1 = rows * cols * samplesPerPixel; - const Uint32 v2 = (bitsAllocated / 8) * v1; - const Uint32 v3 = ((bitsAllocated % 8) * v1 + 7) / 8; - // # old code: frameSize = (bitsAllocated * rows * cols * samplesPerPixel + 7) / 8; - frameSize = v2 + v3; + /* compute frame size */ + if ((effectiveBitsAllocated % 8) == 0) + { + const Uint16 bytesAllocated = effectiveBitsAllocated / 8; + const Uint32 v1 = rows * cols * samplesPerPixel; + frameSize = bytesAllocated * v1; + if (frameSize / bytesAllocated != v1) + { + DCMDATA_WARN("DcmElement: frame size too large, 32-bit integer overflow"); + result = EC_InvalidValue; + } + } + else + { + // Split the calculation in order to avoid integer overflow for large pixel data. + // # old code: frameSize = (effectiveBitsAllocated * rows * cols * samplesPerPixel + 7) / 8; + const Uint32 v1 = rows * cols * samplesPerPixel; + const Uint32 v2 = (effectiveBitsAllocated / 8) * v1; + const Uint32 v3 = ((effectiveBitsAllocated % 8) * v1 + 7) / 8; + frameSize = v2 + v3; + } } - } else { - /* in case of error, return a frame size of 0 */ - frameSize = 0; } } + + /* in case of error, return a frame size of 0 */ + if (result.bad()) frameSize = 0; return result; } diff --git a/dcmdata/libsrc/dcencdoc.cc b/dcmdata/libsrc/dcencdoc.cc index e9073064..4bcf0b69 100644 --- a/dcmdata/libsrc/dcencdoc.cc +++ b/dcmdata/libsrc/dcencdoc.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2018-2024, OFFIS e.V. + * Copyright (C) 2018-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -13,7 +13,7 @@ * * Module: dcmdata * - * Author: Pedro ArizpeGomez + * Author: Pedro ArizpeGomez, Marco Eichelberg * * Purpose: Implementation of Document encapsulation * @@ -40,111 +40,115 @@ #define SHORTCOL 3 #define LONGCOL 21 -// exit codes for this command line tool -// (common codes are defined in "ofexit.h" included from "ofconapp.h") - -// general errors -#define EXITCODE_MEMORY_EXHAUSTED 4 - -DcmEncapsulatedDocument::DcmEncapsulatedDocument() : - opt_patientBirthdate(), - opt_patientID(), - opt_patientName(), - opt_patientSex(), - - opt_conceptCM(), - opt_conceptCSD(), - opt_conceptCV(), - - opt_documentTitle(), - opt_seriesFile(), - opt_seriesUID(), - opt_studyUID(), - - opt_oenctype(EET_ExplicitLength), - opt_writeMode(EWM_fileformat), - opt_oglenc(EGL_withoutGL), - opt_opadenc(EPD_withoutPadding), - opt_oxfer(EXS_LittleEndianExplicit), - opt_filepad(0), - opt_itempad(0), - - opt_readSeriesInfo(OFFalse), - opt_annotation(OFTrue), - opt_increment(OFFalse), - - opt_instance(1), - opt_overrideKeys(), - - cda_mediaTypes(), - hl7_InstanceIdentifier(), - opt_override(OFFalse), - // Frame of Reference Module (STL) - opt_frameOfReferenceUID(), - opt_positionReferenceIndicator(), - // Frame of Reference Module (STL) - opt_manufacturer(), - opt_manufacturerModelName(), - opt_deviceSerialNumber(), - opt_softwareVersions(), - // Enhanced General Equipment Module (STL) - opt_measurementUnitsCM(), - opt_measurementUnitsCSD(), - opt_measurementUnitsCV(), - //encapsulation file type - ftype() +DcmEncapsulatedDocument::DcmEncapsulatedDocument() +: patientBirthdate_() +, patientID_() +, patientName_() +, patientSex_() +, conceptCM_() +, conceptCSD_() +, conceptCV_() +, documentTitle_() +, seriesFile_() +, seriesUID_() +, studyUID_() +, specificCharSet_() +, modality_() +, oenctype_(EET_ExplicitLength) +, writeMode_(EWM_fileformat) +, oglenc_(EGL_withoutGL) +, opadenc_(EPD_withoutPadding) +, oxfer_(EXS_LittleEndianExplicit) +, filepad_(0) +, itempad_(0) +, readSeriesInfo_(OFFalse) +, annotation_(OFTrue) +, increment_(OFFalse) +, instance_(1) +, overrideKeys_() +, cda_mediaTypes() +, hl7_InstanceIdentifier() +, override_(OFFalse) +, frameOfReferenceUID_() +, positionReferenceIndicator_() +, manufacturer_() +, manufacturerModelName_() +, deviceSerialNumber_() +, softwareVersions_() +, measurementUnitsCM_() +, measurementUnitsCSD_() +, measurementUnitsCV_() +, ftype_(DT_unknownDocument) +, dfile_() { } -OFBool DcmEncapsulatedDocument::XMLsearchAttribute( + +DcmEncapsulatedDocument::~DcmEncapsulatedDocument() +{ +} + +// ========== static helper functions for processing CDA documents ========== + +/** Recursive function used by getAttributeValues to get all occurrences of an attribute as a list. + * @param currnode the current XML node to be processed. + * @param list of strings to which the results are added. + * @param attr the attribute to search for. + * @return OFTrue if the attribute value was found, OFFalse otherwise. + */ +static OFBool XMLsearchAttribute( XMLNode currnode, - OFList *results, - OFString attr) + OFList &results, + const OFString& attr) { OFBool found = OFFalse; #ifndef _XMLWIDECHAR if (currnode.nChildNode() == 0) { - //"currnode has no children (leaf)"; + // currnode has no children (leaf) if (currnode.isAttributeSet(attr.c_str())) { //attribute found on leaf - results->push_back(OFSTRING_GUARD(currnode.getAttribute(attr.c_str()))); + results.push_back(OFSTRING_GUARD(currnode.getAttribute(attr.c_str()))); found = OFTrue; } } else { - //"currnode has children (branch)"; + // currnode has children (branch) if (currnode.isAttributeSet(attr.c_str())) { //attribute found on branch - results->push_back(OFSTRING_GUARD(currnode.getAttribute(attr.c_str()))); + results.push_back(OFSTRING_GUARD(currnode.getAttribute(attr.c_str()))); found = OFTrue; } for (int i = 0; i < currnode.nChildNode(); i++) { //search all children recursively - OFBool childfound = XMLsearchAttribute(currnode.getChildNode(i), results, attr); - found |= childfound; + found |= XMLsearchAttribute(currnode.getChildNode(i), results, attr); } } #endif return found; } -OFString DcmEncapsulatedDocument::XMLgetAllAttributeValues( - XMLNode fileNode, - OFString attr) + +/** Retrieve all entries of an attribute and returns them as a string, separated by backslashes. + * @param fileNode the root XML node. + * @param attr the attribute to search for. + * @return OFstring containing all entries found, separated by backslashes + */ +static OFString XMLgetAllAttributeValues(XMLNode& fileNode, const OFString& attr) { OFString attributeValues; #ifndef _XMLWIDECHAR OFList attributeValueslist; - if (XMLsearchAttribute(fileNode, &attributeValueslist, attr)) + if (XMLsearchAttribute(fileNode, attributeValueslist, attr)) { - //If the Attribute is mediaType, initialize with text/xml to exclude - //the primary MIME Type of the encapsulated document + // If the Attribute is mediaType, initialize with text/xml to exclude + // the primary MIME Type of the encapsulated document if (attr == "mediaType") attributeValues.append("text/xml"); + while (!attributeValueslist.empty()) { if (attributeValues.find(attributeValueslist.front()) == OFString_npos) @@ -154,8 +158,8 @@ OFString DcmEncapsulatedDocument::XMLgetAllAttributeValues( } attributeValueslist.pop_front(); } - //remove the primary MIME Type of the - //encapsulated document + + // remove the primary MIME Type of the encapsulated document if (attr == "mediaType") { if (attributeValues.size() > 9) @@ -168,682 +172,642 @@ OFString DcmEncapsulatedDocument::XMLgetAllAttributeValues( return attributeValues; } -OFString DcmEncapsulatedDocument::XMLgetAttribute( - XMLNode fileNode, - DcmTagKey attr) + +/** Retrieve the value from the CDA document corresponding to the given DICOM Tag, + * according to DICOM Part 20, section A.8. + * @param fileNode the root XML node. + * @param attr the tag to search for in the CDA file. + * @return OFstring containing the value of the corresponding tag. + */ +static OFString XMLgetAttribute(XMLNode& fileNode, const DcmTagKey& attr) { - OFString result = ""; #ifndef _XMLWIDECHAR - if (attr == DCM_DocumentTitle) - { - if (fileNode.getChildNode("title").getText() != NULL) + if (attr == DCM_DocumentTitle) { - result = OFString(OFSTRING_GUARD(fileNode.getChildNode("title").getText())); + if (fileNode.getChildNode("title").getText() != NULL) + { + return OFString(OFSTRING_GUARD(fileNode.getChildNode("title").getText())); + } } - } - if (attr == DCM_HL7InstanceIdentifier) - { - result = OFString(OFSTRING_GUARD(fileNode.getChildNode("id").getAttribute("root"))) + "^" - + OFString(OFSTRING_GUARD(fileNode.getChildNode("id").getAttribute("extension"))); - } - /*PatientNameExtension could reflect the type of name (PHON, IDE, ABC) - if (attr == DCM_PatientNameExtension) - { - result = OFString(OFSTRING_GUARD(fileNode.getChildNodeByPath("recordTarget/patientRole/patient/name").getAttribute("use"))); - }*/ - if (attr == DCM_PatientName) - { - result = OFString( - OFSTRING_GUARD(fileNode.getChildNodeByPath("recordTarget/patientRole/patient/name/family").getText())) + "^" - + OFString(OFSTRING_GUARD( - fileNode.getChildNodeByPath("recordTarget/patientRole/patient/name").getChildNode( - "given", 0).getText())) + "^" - + OFString(OFSTRING_GUARD( - fileNode.getChildNodeByPath("recordTarget/patientRole/patient/name").getChildNode( - "given", 1).getText())) + "^" - + OFString( - OFSTRING_GUARD(fileNode.getChildNodeByPath("recordTarget/patientRole/patient/name/prefix").getText())) + "^" - + OFString( - OFSTRING_GUARD(fileNode.getChildNodeByPath("recordTarget/patientRole/patient/name/suffix").getText())); - } - if (attr == DCM_PatientSex) - { - result = OFString(OFSTRING_GUARD(fileNode.getChildNodeByPath( - "recordTarget/patientRole/patient/administrativeGenderCode").getAttribute("code"))); - } - if (attr == DCM_PatientBirthDate) - { - result = OFString(OFSTRING_GUARD( - fileNode.getChildNodeByPath("recordTarget/patientRole/patient/birthTime").getAttribute( - "value"))); - } - //Table A.8-1. Basic Code Attributes Mapping to HL7 V3 Code Data Types (CV, CS, CE and CD) - if (attr == DCM_PatientID) - { - result = OFString( - OFSTRING_GUARD(fileNode.getChildNodeByPath("recordTarget/patientRole/id").getAttribute("extension"))); - } - if (attr == DCM_CodeValue)//Code Value - { - result = OFString(OFSTRING_GUARD(fileNode.getChildNode("code").getAttribute("code"))); - } - if (attr == DCM_CodingSchemeUID)//Coding Scheme UID (PS3.16) - { - result = OFString(OFSTRING_GUARD(fileNode.getChildNode("code").getAttribute("codeSystem"))); - } - if (attr == DCM_CodingSchemeDesignator)//Coding Scheme Designator (0008,0102) - { - OFString CSDtemp = OFString(OFSTRING_GUARD(fileNode.getChildNode("code").getAttribute("codeSystemName"))); - // Abbreviate most common CSNs - if (CSDtemp == OFString("LOINC")) + + if (attr == DCM_HL7InstanceIdentifier) { - result = OFString("LN"); + return OFString(OFSTRING_GUARD(fileNode.getChildNode("id").getAttribute("root"))) + "^" + + OFString(OFSTRING_GUARD(fileNode.getChildNode("id").getAttribute("extension"))); } - else + + if (attr == DCM_PatientName) + { + return OFString( + OFSTRING_GUARD(fileNode.getChildNodeByPath("recordTarget/patientRole/patient/name/family").getText())) + "^" + + OFString(OFSTRING_GUARD(fileNode.getChildNodeByPath("recordTarget/patientRole/patient/name").getChildNode("given", 0).getText())) + "^" + + OFString(OFSTRING_GUARD(fileNode.getChildNodeByPath("recordTarget/patientRole/patient/name").getChildNode("given", 1).getText())) + "^" + + OFString(OFSTRING_GUARD(fileNode.getChildNodeByPath("recordTarget/patientRole/patient/name/prefix").getText())) + "^" + + OFString(OFSTRING_GUARD(fileNode.getChildNodeByPath("recordTarget/patientRole/patient/name/suffix").getText())); + } + + if (attr == DCM_PatientSex) + { + return OFString(OFSTRING_GUARD(fileNode.getChildNodeByPath("recordTarget/patientRole/patient/administrativeGenderCode").getAttribute("code"))); + } + + if (attr == DCM_PatientBirthDate) + { + return OFString(OFSTRING_GUARD(fileNode.getChildNodeByPath("recordTarget/patientRole/patient/birthTime").getAttribute("value"))); + } + + //Table A.8-1. Basic Code Attributes Mapping to HL7 V3 Code Data Types (CV, CS, CE and CD) + if (attr == DCM_PatientID) + { + return OFString(OFSTRING_GUARD(fileNode.getChildNodeByPath("recordTarget/patientRole/id").getAttribute("extension"))); + } + + if (attr == DCM_CodeValue) { - if (CSDtemp == OFString("DICOM")) + return OFString(OFSTRING_GUARD(fileNode.getChildNode("code").getAttribute("code"))); + } + if (attr == DCM_CodingSchemeUID) + { + return OFString(OFSTRING_GUARD(fileNode.getChildNode("code").getAttribute("codeSystem"))); + } + if (attr == DCM_CodingSchemeDesignator) + { + OFString CSDtemp = OFString(OFSTRING_GUARD(fileNode.getChildNode("code").getAttribute("codeSystemName"))); + + // Abbreviate most common CSNs + if (CSDtemp == OFString("LOINC")) { - result = OFString("DC"); + return OFString("LN"); } else { - if (CSDtemp == OFString("SNOMED")) + if (CSDtemp == OFString("DICOM")) { - result = OFString("SRT"); + return OFString("DCM"); } else { - result = CSDtemp; + if (CSDtemp == OFString("SNOMED")) + { + return OFString("SCT"); + } + else + { + return CSDtemp; + } } } } - } - if (attr == DCM_CodingSchemeVersion)//Coding Scheme Version (0008,0103) - { - result = OFString(OFSTRING_GUARD(fileNode.getChildNode("code").getAttribute("codeSystemVersion"))); - } - if (attr == DCM_CodeMeaning)//Code Meaning (0008,0104) - { - result = OFString(OFSTRING_GUARD(fileNode.getChildNode("code").getAttribute("displayName"))); - } + + if (attr == DCM_CodingSchemeVersion) + { + return OFString(OFSTRING_GUARD(fileNode.getChildNode("code").getAttribute("codeSystemVersion"))); + } + + if (attr == DCM_CodeMeaning) + { + return OFString(OFSTRING_GUARD(fileNode.getChildNode("code").getAttribute("displayName"))); + } + + // this shouldn't happen + DCMDATA_ERROR("XMLgetAttribute called with unsupported attribute tag " << attr); #endif - return result; + + return ""; } -int DcmEncapsulatedDocument::getCDAData( - const char *filename, - OFLogger &appLogger) +// ========== class methods ========== + +OFCondition DcmEncapsulatedDocument::getCDAData() { #ifdef _XMLWIDECHAR #ifdef _MSC_VER -#pragma message("DCMTK compiled with 'wide char XML parser'. cda2dcm will be unable to read and encapsulate CDA documents.") +#pragma message("DCMTK compiled with 'wide char XML parser'. dcmencap will be unable to read and encapsulate CDA documents.") #else -#warning "DCMTK compiled with 'wide char XML parser'. cda2dcm will be unable to read and encapsulate CDA documents." +#warning "DCMTK compiled with 'wide char XML parser'. dcmencap will be unable to read and encapsulate CDA documents." #endif - OFLOG_ERROR(appLogger, "DCMTK compiled with \"wide char XML parser\". Cannot parse CDA data because of incompatible API."); - return 99; + DCMDATA_ERROR("DCMTK compiled with \"wide char XML parser\". Cannot parse CDA data because of incompatible API."); + return EC_XMLParseError; #else - if (ftype != "cda") - { - OFLOG_WARN(appLogger, "Filetype mismatch or filetype not set. Current ftype is " << ftype); - } - XMLResults err; - XMLNode fileNode = XMLNode::parseFile(filename, "ClinicalDocument", &err); - OFLOG_TRACE(appLogger, "checking if the XML file is correctly formatted"); - if (0 != err.error) - { - OFLOG_ERROR(appLogger, fileNode.getError(err.error)); - return EXITCODE_INVALID_INPUT_FILE; - } - else - { - OFLOG_TRACE(appLogger, "XML file is correctly formatted"); - } - OFLOG_TRACE(appLogger, "Getting all media types from CDA file"); - cda_mediaTypes = XMLgetAllAttributeValues(fileNode, "mediaType"); - OFLOG_TRACE(appLogger, "Following mediaTypes were found: " << cda_mediaTypes); - OFLOG_TRACE(appLogger, "Getting HL7 Instance Identifier from CDA file"); - hl7_InstanceIdentifier = XMLgetAttribute(fileNode, DCM_HL7InstanceIdentifier); - OFLOG_TRACE(appLogger, "Reading and comparing patient information between CDA File and options"); - OFString pID = XMLgetAttribute(fileNode, DCM_PatientID); - if ((pID != "") && (opt_patientID != pID)) - { - if (opt_patientID != "") + DCMDATA_INFO("Extracting information from CDA document content"); + + XMLResults err; + XMLNode fileNode = XMLNode::parseFile(ifname_.c_str(), "ClinicalDocument", &err); + DCMDATA_TRACE("checking if the XML file is correctly formatted"); + if (0 != err.error) { - //if no-override option is inactive, return an error - if (!opt_override) - { - OFLOG_ERROR(appLogger, "Patient ID mismatch:" << OFendl - << "Found in the CDA file : " << pID << OFendl - << "Entered (or found in DCM file): " << opt_patientID << OFendl - << "If you wish to override, run again with +ov"); - return EXITCODE_COMMANDLINE_SYNTAX_ERROR; - } - else - { - OFLOG_WARN(appLogger, "Patient ID mismatch:" << OFendl - << "Found in the CDA file : " << pID << OFendl - << "Provided (in DCM file): " << opt_patientID); - } + DCMDATA_ERROR(fileNode.getError(err.error)); + return EC_XMLParseError; } else { - opt_patientID = pID; + DCMDATA_TRACE("XML file is correctly formatted"); } - } - OFString pBirthDate = XMLgetAttribute(fileNode, DCM_PatientBirthDate); - if ((pBirthDate != "") && (opt_patientBirthdate != pBirthDate)) - { - if (opt_patientBirthdate != "") + DCMDATA_TRACE("Getting all media types from CDA file"); + cda_mediaTypes = XMLgetAllAttributeValues(fileNode, "mediaType"); + DCMDATA_TRACE("Following mediaTypes were found: " << cda_mediaTypes); + DCMDATA_TRACE("Getting HL7 Instance Identifier from CDA file"); + hl7_InstanceIdentifier = XMLgetAttribute(fileNode, DCM_HL7InstanceIdentifier); + DCMDATA_TRACE("Reading and comparing patient information between CDA File and options"); + OFString pID = XMLgetAttribute(fileNode, DCM_PatientID); + if ((pID.length() > 0) && (patientID_ != pID)) { - if (!opt_override) + if (patientID_.length() > 0) { - OFLOG_ERROR(appLogger, "Patient Birth Date mismatch:" << OFendl - << "Found in the CDA file : " << pBirthDate << OFendl - << "Provided (in DCM file): " << opt_patientBirthdate << OFendl - << "If you wish to override, run again with +ov"); - return EXITCODE_COMMANDLINE_SYNTAX_ERROR; + if (override_) DCMDATA_WARN("Patient ID mismatch: '" << pID << "' in CDA, '" << patientID_ << "' in DICOM file or specified via command line."); + else + { + DCMDATA_ERROR("Patient ID mismatch: '" << pID << "' in CDA, '" << patientID_ << "' in DICOM file or specified via command line."); + return EC_InvalidValue; + } } else { - OFLOG_WARN(appLogger, "Patient Birth Date mismatch:" << OFendl - << "Found in the CDA file : " << pBirthDate << OFendl - << "Provided (in DCM file): " << opt_patientBirthdate); + patientID_ = pID; } } - else opt_patientBirthdate = pBirthDate; - } - OFString pSex = XMLgetAttribute(fileNode, DCM_PatientSex); - if ((pSex != "") && (opt_patientSex != pSex)) - { - if (opt_patientSex != "") + OFString pBirthDate = XMLgetAttribute(fileNode, DCM_PatientBirthDate); + if ((pBirthDate.length() > 0) && (patientBirthdate_ != pBirthDate)) { - if (!opt_override) - { - OFLOG_ERROR(appLogger, "Patient Sex mismatch:" << OFendl - << "Found in the CDA file : " << pSex << OFendl - << "Provided (in DCM file): " << opt_patientSex << OFendl - << "If you wish to override, run again with +ov"); - return EXITCODE_COMMANDLINE_SYNTAX_ERROR; - } - else + if (patientBirthdate_.length() > 0) { - OFLOG_WARN(appLogger, "Patient Sex mismatch:" << OFendl - << "Found in the CDA file : " << pSex << OFendl - << "Provided (in DCM file): " << opt_patientSex); + if (override_) DCMDATA_WARN("Patient Birth Date mismatch: '" << pBirthDate << "' in CDA, '" << patientBirthdate_ << "' in DICOM file or specified via command line."); + else + { + DCMDATA_ERROR("Patient Birth Date mismatch: '" << pBirthDate << "' in CDA, '" << patientBirthdate_ << "' in DICOM file or specified via command line."); + return EC_InvalidValue; + } } + else patientBirthdate_ = pBirthDate; } - else opt_patientSex = pSex; - } - OFString pName = XMLgetAttribute(fileNode, DCM_PatientName); - if ((pName != "^^^^") && (opt_patientName != pName)) - { - if (opt_patientName != "") + OFString pSex = XMLgetAttribute(fileNode, DCM_PatientSex); + if ((pSex.length() > 0) && (patientSex_ != pSex)) { - if (!opt_override) - { - OFLOG_ERROR(appLogger, "Patient Name mismatch:" << OFendl - << "Found in the CDA file : " << pName << OFendl - << "Provided (in DCM file): " << opt_patientName << OFendl - << "If you wish to override, run again with +ov"); - return EXITCODE_COMMANDLINE_SYNTAX_ERROR; - } - else + if (patientSex_.length() > 0) { - OFLOG_WARN(appLogger, "Patient Name mismatch:" << OFendl - << "Found in the CDA file : " << pName << OFendl - << "Provided (in DCM file): " << opt_patientName); + if (override_) DCMDATA_WARN("Patient Sex mismatch: '" << pSex << "' in CDA, '" << patientSex_ << "' in DICOM file or specified via command line."); + else + { + DCMDATA_ERROR("Patient Sex mismatch: '" << pSex << "' in CDA, '" << patientSex_ << "' in DICOM file or specified via command line."); + return EC_InvalidValue; + } } + else patientSex_ = pSex; } - else opt_patientName = pName; - } - //get document title from CDA - OFString dTitle = XMLgetAttribute(fileNode, DCM_DocumentTitle); - if (opt_documentTitle == "") - { - if (opt_conceptCSD != "") opt_documentTitle = opt_conceptCSD; - if (opt_conceptCV != "") opt_documentTitle = opt_conceptCV; - if (opt_conceptCM != "") opt_documentTitle = opt_conceptCM; - } - - if ((dTitle != "") && (opt_documentTitle != dTitle)) - { - if (opt_documentTitle != "") + OFString pName = XMLgetAttribute(fileNode, DCM_PatientName); + if ((pName != "^^^^") && (patientName_ != pName)) { - if (!opt_override) + if (patientName_.length() > 0) { - OFLOG_ERROR(appLogger, "Document Title mismatch:" << OFendl - << "Found in the CDA file : " << dTitle << OFendl - << "Provided (in DCM file): " << opt_documentTitle << OFendl - << "If you wish to override, run again with +ov"); - return EXITCODE_COMMANDLINE_SYNTAX_ERROR; - } - else - { - OFLOG_WARN(appLogger, "Document Title mismatch:" << OFendl - << "Found in the CDA file : " << dTitle << OFendl - << "Provided (in DCM file): " << opt_documentTitle); + if (override_) DCMDATA_WARN("Patient Name mismatch: '" << pName << "' in CDA, '" << patientName_ << "' in DICOM file or specified via command line."); + else + { + DCMDATA_ERROR("Patient Name mismatch: '" << pName << "' in CDA, '" << patientName_ << "' in DICOM file or specified via command line."); + return EC_InvalidValue; + } } + else patientName_ = pName; } - else opt_documentTitle = dTitle; - } - //get Concept information from CDA - OFString cCSD = XMLgetAttribute(fileNode, DCM_CodingSchemeDesignator); - if ((cCSD != "") && (opt_conceptCSD != cCSD)) - { - if (opt_conceptCSD != "") + //get document title from CDA + OFString dTitle = XMLgetAttribute(fileNode, DCM_DocumentTitle); + if ((dTitle.length() > 0) && (documentTitle_ != dTitle)) { - if (!opt_override) + if (documentTitle_.length() > 0) { - OFLOG_ERROR(appLogger, "concept CSD mismatch:" << OFendl - << "Found in the CDA file : " << cCSD << OFendl - << "Provided (in DCM file): " << opt_conceptCSD << OFendl - << "If you wish to override, run again with +ov"); - return EXITCODE_COMMANDLINE_SYNTAX_ERROR; - } - else - { - OFLOG_WARN(appLogger, "concept CSD mismatch:" << OFendl - << "Found in the CDA file : " << cCSD << OFendl - << "Provided (in DCM file): " << opt_conceptCSD); + if (override_) DCMDATA_WARN("Document Title mismatch: '" << dTitle << "' in CDA, '" << documentTitle_ << "' in DICOM file or specified via command line."); + else + { + DCMDATA_ERROR("Document Title mismatch: '" << dTitle << "' in CDA, '" << documentTitle_ << "' in DICOM file or specified via command line."); + return EC_InvalidValue; + } } + else documentTitle_ = dTitle; } - else opt_conceptCSD = cCSD; - } - OFString cCV = XMLgetAttribute(fileNode, DCM_CodeValue); - if ((cCV != "") && (opt_conceptCV != cCV)) - { - if (opt_conceptCV != "") + + //get Concept information from CDA + OFString cCSD = XMLgetAttribute(fileNode, DCM_CodingSchemeDesignator); + if ((cCSD.length() > 0) && (conceptCSD_ != cCSD)) { - if (!opt_override) - { - OFLOG_ERROR(appLogger, "concept CV mismatch:" << OFendl - << "Found in the CDA file : " << cCV << OFendl - << "Provided (in DCM file): " << opt_conceptCV << OFendl - << "If you wish to override, run again with +ov"); - return EXITCODE_COMMANDLINE_SYNTAX_ERROR; - } - else + if (conceptCSD_.length() > 0) { - OFLOG_WARN(appLogger, "concept CV mismatch:" << OFendl - << "Found in the CDA file : " << cCV << OFendl - << "Provided (in DCM file): " << opt_conceptCV); + if (override_) DCMDATA_WARN("concept CSD mismatch: '" << cCSD << "' in CDA, '" << conceptCSD_ << "' in DICOM file or specified via command line."); + else + { + DCMDATA_ERROR("concept CSD mismatch: '" << cCSD << "' in CDA, '" << conceptCSD_ << "' in DICOM file or specified via command line."); + return EC_InvalidValue; + } } + else conceptCSD_ = cCSD; } - else opt_conceptCV = cCV; - } - OFString cCM = XMLgetAttribute(fileNode, DCM_CodeMeaning); - if ((cCM != "") && (opt_conceptCM != cCM)) - { - if (opt_conceptCM != "") + OFString cCV = XMLgetAttribute(fileNode, DCM_CodeValue); + if ((cCV.length() > 0) && (conceptCV_ != cCV)) { - if (!opt_override) + if (conceptCV_.length() > 0) { - OFLOG_ERROR(appLogger, "concept CM mismatch:" << OFendl - << "Found in the CDA file : " << cCM << OFendl - << "Provided (in DCM file): " << opt_conceptCM << OFendl - << "If you wish to override, run again with +ov"); - return EXITCODE_COMMANDLINE_SYNTAX_ERROR; + if (override_) DCMDATA_WARN("concept CV mismatch: '" << cCV << "' in CDA, '" << conceptCV_ << "' in DICOM file or specified via command line."); + else + { + DCMDATA_ERROR("concept CV mismatch: '" << cCV << "' in CDA, '" << conceptCV_ << "' in DICOM file or specified via command line."); + return EC_InvalidValue; + } } - else + else conceptCV_ = cCV; + } + OFString cCM = XMLgetAttribute(fileNode, DCM_CodeMeaning); + if ((cCM.length() > 0) && (conceptCM_ != cCM)) + { + if (conceptCM_.length() > 0) { - OFLOG_WARN(appLogger, "concept CM mismatch:" << OFendl - << "Found in the CDA file : " << cCM << OFendl - << "Provided (in DCM file): " << opt_conceptCM); + if (override_) DCMDATA_WARN("concept CM mismatch: '" << cCM << "' in CDA, '" << conceptCM_ << "' in DICOM file or specified via command line."); + else + { + DCMDATA_ERROR("concept CM mismatch: '" << cCM << "' in CDA, '" << conceptCM_ << "' in DICOM file or specified via command line."); + return EC_InvalidValue; + } } + else conceptCM_ = cCM; } - else opt_conceptCM = cCM; - } - return EXITCODE_NO_ERROR; + return EC_Normal; #endif } -void DcmEncapsulatedDocument::addCDACommandlineOptions(OFCommandLine &cmd) -{ - ftype = "cda"; - cmd.setOptionColumns(LONGCOL, SHORTCOL); - cmd.setParamColumn(LONGCOL + SHORTCOL + 4); - cmd.addParam("cdafile-in", "CDA input filename to be converted"); - cmd.addParam("dcmfile-out", "DICOM output filename (\"-\" for stdout)"); - addGeneralOptions(cmd); - addDocumentOptions(cmd); - cmd.addSubGroup("override CDA data:"); - cmd.addOption("--no-override", "-ov", - "CDA patient and document data must match study,\nseries or manually entered information (default)"); - cmd.addOption("--override", "+ov", - "CDA's data will be overwritten by study, series\nor manually entered information"); - addOutputOptions(cmd); -} -void DcmEncapsulatedDocument::addPDFCommandlineOptions(OFCommandLine &cmd) +static void addGeneralOptions(OFCommandLine &cmd) { - ftype = "pdf"; - cmd.setOptionColumns(LONGCOL, SHORTCOL); - cmd.setParamColumn(LONGCOL + SHORTCOL + 4); - cmd.addParam("pdffile-in", "PDF input filename to be converted"); - cmd.addParam("dcmfile-out", "DICOM output filename (\"-\" for stdout)"); - addGeneralOptions(cmd); - addDocumentOptions(cmd); - addOutputOptions(cmd); + cmd.addGroup("general options:", LONGCOL, SHORTCOL + 2); + cmd.addOption("--help", "-h", "print this help text and exit", OFCommandLine::AF_Exclusive); + cmd.addOption("--version", "print version information and exit", OFCommandLine::AF_Exclusive); + OFLog::addOptions(cmd); } -void DcmEncapsulatedDocument::addSTLCommandlineOptions(OFCommandLine &cmd) -{ - ftype = "stl"; - cmd.setOptionColumns(LONGCOL, SHORTCOL); - cmd.setParamColumn(LONGCOL + SHORTCOL + 4); - cmd.addParam("stlfile-in", "STL input filename to be converted"); - cmd.addParam("dcmfile-out", "DICOM output filename (\"-\" for stdout)"); - addGeneralOptions(cmd); - addDocumentOptions(cmd); - cmd.addSubGroup("enhanced general equipment:"); - cmd.addOption("--manufacturer", "+mn", 1, "[n]ame: string", - "manufacturer's name"); - cmd.addOption("--manufacturer-model", "+mm", 1, "[n]ame: string", - "manufacturer's model name"); - cmd.addOption("--device-serial", "+ds", 1, "[n]umber: string", - "device serial number"); - cmd.addOption("--software-versions", "+sv", 1, "[v]ersions: string", - "software versions"); - cmd.addSubGroup("3d model measurement units:"); - cmd.addOption("--measurement-units", "+mu", 3, "[CSD] [CV] [CM]: string (default: UCUM, um, um)", - "measurement units defined by coding scheme\ndesignator CSD, code value CV, code meaning CM"); - addOutputOptions(cmd); -} -void DcmEncapsulatedDocument::addGeneralOptions(OFCommandLine &cmd) +static void addDocumentOptions(OFCommandLine &cmd) { - cmd.addGroup("general options:", LONGCOL, SHORTCOL + 2); - cmd.addOption("--help", "-h", "print this help text and exit", OFCommandLine::AF_Exclusive); - cmd.addOption("--version", "print version information and exit", OFCommandLine::AF_Exclusive); - OFLog::addOptions(cmd); + cmd.addGroup("DICOM document options:"); + + cmd.addSubGroup("document title:"); + cmd.addOption("--title", "+t", 1, "[t]itle: string (default: empty)", "document title"); + cmd.addOption("--concept-name", "+cn", 3, "[CSD] [CV] [CM]: string (default: empty)", + "coded representation of document title defined\nby coding scheme designator CSD,\n" + "code value CV and code meaning CM"); + cmd.addSubGroup("patient data:"); + cmd.addOption("--patient-name", "+pn", 1, "[n]ame: string", "patient's name in DICOM PN syntax"); + cmd.addOption("--patient-id", "+pi", 1, "[i]d: string", "patient identifier"); + cmd.addOption("--patient-birthdate", "+pb", 1, "[d]ate: string (YYYYMMDD)", "patient's birth date"); + cmd.addOption("--patient-sex", "+ps", 1, "[s]ex: string (M, F or O)", "patient's sex"); + + cmd.addSubGroup("device data:"); + cmd.addOption("--manufacturer", "+mn", 1, "[n]ame: string", "manufacturer's name"); + cmd.addOption("--manufacturer-model", "+mm", 1, "[n]ame: string", "manufacturer's model name"); + cmd.addOption("--device-serial", "+ds", 1, "[n]umber: string", "device serial number"); + cmd.addOption("--software-versions", "+sv", 1, "[v]ersions: string", "software versions"); + + cmd.addSubGroup("manufacturing 3d model data (STL/MTL/OBJ only):"); + cmd.addOption("--measurement-units", "+mu", 3, "[CSD] [CV] [CM]: string (default: UCUM, um, um)", + "measurement units defined by coding scheme\ndesignator CSD, code value CV, code meaning CM"); + + cmd.addSubGroup("study and series:"); + cmd.addOption("--generate", "+sg", "generate new study and\nseries UIDs (default)"); + cmd.addOption("--study-from", "+st", 1, "[f]ilename: string", "read patient/study data from DICOM file"); + cmd.addOption("--series-from", "+se", 1, "[f]ilename: string", "read patient/study/series data from DICOM file"); + + cmd.addSubGroup("instance number:"); + cmd.addOption("--instance-one", "+i1", "use instance number 1\n(default, not with +se)"); + cmd.addOption("--instance-inc", "+ii", "increment instance number (only with +se)"); + cmd.addOption("--instance-set", "+is", 1, "[i]nstance number: integer", "use instance number i"); + + cmd.addSubGroup("burned-in annotation:"); + cmd.addOption("--annotation-yes", "+an", "document contains patient identifying data\n(default)"); + cmd.addOption("--annotation-no", "-an", "document does not contain patient identif. data"); } -void DcmEncapsulatedDocument::addDocumentOptions(OFCommandLine &cmd) + +static void addOutputOptions(OFCommandLine &cmd) { - cmd.addGroup("DICOM document options:"); - cmd.addSubGroup("document title:"); - cmd.addOption("--title", "+t", 1, "[t]itle: string (default: empty)", - "document title"); - cmd.addOption("--concept-name", "+cn", 3, "[CSD] [CV] [CM]: string (default: empty)", - "coded representation of document title defined\nby coding scheme designator CSD,\n" - "code value CV and code meaning CM"); - cmd.addSubGroup("patient data:"); - cmd.addOption("--patient-name", "+pn", 1, "[n]ame: string", - "patient's name in DICOM PN syntax"); - cmd.addOption("--patient-id", "+pi", 1, "[i]d: string", - "patient identifier"); - cmd.addOption("--patient-birthdate", "+pb", 1, "[d]ate: string (YYYYMMDD)", - "patient's birth date"); - cmd.addOption("--patient-sex", "+ps", 1, "[s]ex: string (M, F or O)", - "patient's sex"); - cmd.addSubGroup("study and series:"); - cmd.addOption("--generate", "+sg", "generate new study and\nseries UIDs (default)"); - cmd.addOption("--study-from", "+st", 1, "[f]ilename: string", - "read patient/study data from DICOM file"); - cmd.addOption("--series-from", "+se", 1, "[f]ilename: string", - "read patient/study/series data from DICOM file"); - cmd.addSubGroup("instance number:"); - cmd.addOption("--instance-one", "+i1", "use instance number 1\n(default, not with +se)"); - cmd.addOption("--instance-inc", "+ii", "increment instance number (only with +se)"); - cmd.addOption("--instance-set", "+is", 1, "[i]nstance number: integer", "use instance number i"); - cmd.addSubGroup("burned-in annotation:"); - cmd.addOption("--annotation-yes", "+an", "document contains patient identifying data\n(default)"); - cmd.addOption("--annotation-no", "-an", "document does not contain patient identif. data"); + cmd.addGroup("processing options:"); + cmd.addSubGroup("CDA processing options:"); + cmd.addOption("--no-override", "-ov", "CDA patient and document data must match study,\nseries or manually entered information (default)"); + cmd.addOption("--override", "+ov", "CDA's data will be overwritten by study, series\nor manually entered information"); + + cmd.addSubGroup("other processing options:"); + cmd.addOption("--key", "-k", 1, "[k]ey: gggg,eeee=\"str\", path or dict. name=\"str\"", "add further attribute"); + + cmd.addGroup("output options:"); + + cmd.addSubGroup("output transfer syntax:"); + cmd.addOption("--write-xfer-little", "+te", "write with explicit VR little endian (default)"); + cmd.addOption("--write-xfer-big", "+tb", "write with explicit VR big endian TS"); + cmd.addOption("--write-xfer-implicit", "+ti", "write with implicit VR little endian TS"); + + cmd.addSubGroup("group length encoding:"); + cmd.addOption("--group-length-remove", "-g", "write without group length elements (default)"); + cmd.addOption("--group-length-create", "+g", "write with group length elements"); + + cmd.addSubGroup("length encoding in sequences and items:"); + cmd.addOption("--length-explicit", "+e", "write with explicit lengths (default)"); + cmd.addOption("--length-undefined", "-e", "write with undefined lengths"); + + cmd.addSubGroup("data set trailing padding (not with --write-dataset):"); + cmd.addOption("--padding-retain", "-p=", "do not change padding (default)"); + cmd.addOption("--padding-off", "-p", "no padding (implicit if --write-dataset)"); + cmd.addOption("--padding-create", "+p", 2, "[f]ile-pad [i]tem-pad: integer", + "align file on multiple of f bytes\nand items on multiple of i bytes"); } -void DcmEncapsulatedDocument::addOutputOptions(OFCommandLine &cmd) + +void DcmEncapsulatedDocument::addCommandlineOptions(OFCommandLine &cmd) const { - cmd.addGroup("processing options:"); - cmd.addSubGroup("other processing options:"); - cmd.addOption("--key", "-k", 1, "[k]ey: gggg,eeee=\"str\", path or dict. name=\"str\"", - "add further attribute"); - cmd.addGroup("output options:"); - cmd.addSubGroup("output transfer syntax:"); - cmd.addOption("--write-xfer-little", "+te", "write with explicit VR little endian (default)"); - cmd.addOption("--write-xfer-big", "+tb", "write with explicit VR big endian TS"); - cmd.addOption("--write-xfer-implicit", "+ti", "write with implicit VR little endian TS"); - cmd.addSubGroup("group length encoding:"); - cmd.addOption("--group-length-recalc", "+g=", "recalculate group lengths if present (default)"); - cmd.addOption("--group-length-create", "+g", "always write with group length elements"); - cmd.addOption("--group-length-remove", "-g", "always write without group length elements"); - cmd.addSubGroup("length encoding in sequences and items:"); - cmd.addOption("--length-explicit", "+e", "write with explicit lengths (default)"); - cmd.addOption("--length-undefined", "-e", "write with undefined lengths"); - cmd.addSubGroup("data set trailing padding (not with --write-dataset):"); - cmd.addOption("--padding-retain", "-p=", "do not change padding (default)"); - cmd.addOption("--padding-off", "-p", "no padding (implicit if --write-dataset)"); - cmd.addOption("--padding-create", "+p", 2, "[f]ile-pad [i]tem-pad: integer", - "align file on multiple of f bytes\nand items on multiple of i bytes"); + cmd.setOptionColumns(LONGCOL, SHORTCOL); + cmd.setParamColumn(LONGCOL + SHORTCOL + 4); + cmd.addParam("docfile-in", "input filename to be converted"); + cmd.addParam("dcmfile-out", "DICOM output filename (\"-\" for stdout)"); + + addGeneralOptions(cmd); + + cmd.addGroup("input options:", LONGCOL, SHORTCOL + 2); + cmd.addSubGroup("input file format options:"); + cmd.addOption("--filetype-auto", "+fa", "automatically determine file type (default)"); + cmd.addOption("--filetype-pdf", "+fp", "expect PDF file"); + cmd.addOption("--filetype-cda", "+fc", "expect CDA file"); + cmd.addOption("--filetype-stl", "+fs", "expect STL file"); + cmd.addOption("--filetype-mtl", "+fm", "expect MTL file"); + cmd.addOption("--filetype-obj", "+fo", "expect OBJ file"); + + addDocumentOptions(cmd); + + addOutputOptions(cmd); } + void DcmEncapsulatedDocument::parseArguments( OFConsoleApplication &app, OFCommandLine &cmd) { - //command line parameters and options - cmd.getParam(1, opt_ifname); - cmd.getParam(2, opt_ofname); - - OFLog::configureFromCommandLine(cmd, app); - - dcmEnableGenerationOfNewVRs(); + //command line parameters and options + cmd.getParam(1, ifname_); + cmd.getParam(2, ofname_); - // Override keys are applied at the very end of the conversion "pipeline" - OFList overrideKeys; + // ---------- parse general options ---------- + OFLog::configureFromCommandLine(cmd, app); - cmd.beginOptionBlock(); - if (cmd.findOption("--generate")) - { - opt_seriesFile = ""; - opt_readSeriesInfo = OFFalse; - } + // ---------- parse input file format options ---------- + cmd.beginOptionBlock(); + if (cmd.findOption("--filetype-auto")) ftype_ = DT_unknownDocument; + if (cmd.findOption("--filetype-pdf")) ftype_ = DT_pdfDocument; + if (cmd.findOption("--filetype-cda")) ftype_ = DT_cdaDocument; + if (cmd.findOption("--filetype-stl")) ftype_ = DT_stlDocument; + if (cmd.findOption("--filetype-mtl")) ftype_ = DT_mtlDocument; + if (cmd.findOption("--filetype-obj")) ftype_ = DT_objDocument; + cmd.endOptionBlock(); - if (cmd.findOption("--series-from")) - { - app.checkValue(cmd.getValue(opt_seriesFile)); - opt_readSeriesInfo = OFTrue; - } + // ---------- parse document options ---------- - if (cmd.findOption("--study-from")) - { - app.checkValue(cmd.getValue(opt_seriesFile)); - opt_readSeriesInfo = OFFalse; - } - cmd.endOptionBlock(); - if (cmd.findOption("--title")) - { - app.checkValue(cmd.getValue(opt_documentTitle)); - } - if (cmd.findOption("--concept-name")) - { - app.checkValue(cmd.getValue(opt_conceptCSD)); - app.checkValue(cmd.getValue(opt_conceptCV)); - app.checkValue(cmd.getValue(opt_conceptCM)); - } - if (cmd.findOption("--patient-name")) - { - app.checkValue(cmd.getValue(opt_patientName)); - app.checkConflict("--patient-name", "--study-from or --series-from", - opt_seriesFile != ""); - } - if (cmd.findOption("--patient-id")) - { - app.checkValue(cmd.getValue(opt_patientID)); - app.checkConflict("--patient-id", "--study-from or --series-from", - opt_seriesFile != ""); - } - if (cmd.findOption("--patient-birthdate")) - { - app.checkValue(cmd.getValue(opt_patientBirthdate)); - app.checkConflict("--patient-birthdate", "--study-from or --series-from", opt_seriesFile != ""); - } - if (cmd.findOption("--patient-sex")) - { - app.checkValue(cmd.getValue(opt_patientSex)); - app.checkConflict("--patient-sex", "--study-from or --series-from", opt_seriesFile != ""); - } - cmd.beginOptionBlock(); - if (cmd.findOption("--annotation-yes")) - { - opt_annotation = OFTrue; - } - if (cmd.findOption("--annotation-no")) - { - opt_annotation = OFFalse; - } - cmd.endOptionBlock(); - if (ftype == "cda") - { cmd.beginOptionBlock(); - if (cmd.findOption("--override")) + if (cmd.findOption("--generate")) { - opt_override = OFTrue; + seriesFile_ = ""; + readSeriesInfo_ = OFFalse; } - if (cmd.findOption("--no-override")) + + if (cmd.findOption("--series-from")) { - opt_override = OFFalse; + app.checkValue(cmd.getValue(seriesFile_)); + readSeriesInfo_ = OFTrue; + } + + if (cmd.findOption("--study-from")) + { + app.checkValue(cmd.getValue(seriesFile_)); + readSeriesInfo_ = OFFalse; } cmd.endOptionBlock(); - } - if (ftype == "stl") - { - if (cmd.findOption("--measurement-units")) + + if (cmd.findOption("--title")) { - app.checkValue(cmd.getValue(opt_measurementUnitsCSD)); - app.checkValue(cmd.getValue(opt_measurementUnitsCV)); - app.checkValue(cmd.getValue(opt_measurementUnitsCM)); + app.checkValue(cmd.getValue(documentTitle_)); + } + if (cmd.findOption("--concept-name")) + { + app.checkValue(cmd.getValue(conceptCSD_)); + app.checkValue(cmd.getValue(conceptCV_)); + app.checkValue(cmd.getValue(conceptCM_)); + } + if (cmd.findOption("--patient-name")) + { + app.checkValue(cmd.getValue(patientName_)); + app.checkConflict("--patient-name", "--study-from or --series-from", seriesFile_.length() > 0); + } + if (cmd.findOption("--patient-id")) + { + app.checkValue(cmd.getValue(patientID_)); + app.checkConflict("--patient-id", "--study-from or --series-from", seriesFile_.length() > 0); + } + if (cmd.findOption("--patient-birthdate")) + { + app.checkValue(cmd.getValue(patientBirthdate_)); + app.checkConflict("--patient-birthdate", "--study-from or --series-from", seriesFile_.length() > 0); + } + if (cmd.findOption("--patient-sex")) + { + app.checkValue(cmd.getValue(patientSex_)); + app.checkConflict("--patient-sex", "--study-from or --series-from", seriesFile_.length() > 0); } - if (cmd.findOption("--manufacturer")) app.checkValue(cmd.getValue(opt_manufacturer)); - if (cmd.findOption("--manufacturer-model")) app.checkValue(cmd.getValue(opt_manufacturerModelName)); - if (cmd.findOption("--device-serial")) app.checkValue(cmd.getValue(opt_deviceSerialNumber)); - if (cmd.findOption("--software-versions")) app.checkValue(cmd.getValue(opt_softwareVersions)); - } - cmd.beginOptionBlock(); - if (cmd.findOption("--write-xfer-little")) opt_oxfer = EXS_LittleEndianExplicit; - if (cmd.findOption("--write-xfer-big")) opt_oxfer = EXS_BigEndianExplicit; - if (cmd.findOption("--write-xfer-implicit")) opt_oxfer = EXS_LittleEndianImplicit; - cmd.endOptionBlock(); - - cmd.beginOptionBlock(); - if (cmd.findOption("--group-length-recalc")) opt_oglenc = EGL_recalcGL; - if (cmd.findOption("--group-length-create")) opt_oglenc = EGL_withGL; - if (cmd.findOption("--group-length-remove")) opt_oglenc = EGL_withoutGL; - cmd.endOptionBlock(); - - cmd.beginOptionBlock(); - if (cmd.findOption("--length-explicit")) opt_oenctype = EET_ExplicitLength; - if (cmd.findOption("--length-undefined")) opt_oenctype = EET_UndefinedLength; - cmd.endOptionBlock(); - - cmd.beginOptionBlock(); - if (cmd.findOption("--padding-retain")) - { - app.checkConflict("--padding-retain", "--write-dataset", - opt_writeMode == EWM_dataset); - opt_opadenc = EPD_noChange; - } - if (cmd.findOption("--padding-off")) opt_opadenc = EPD_withoutPadding; - if (cmd.findOption("--padding-create")) - { - app.checkConflict("--padding-create", "--write-dataset", - opt_writeMode == EWM_dataset); - app.checkValue(cmd.getValueAndCheckMin(opt_filepad, 0)); - app.checkValue(cmd.getValueAndCheckMin(opt_itempad, 0)); - opt_opadenc = EPD_withPadding; - } - cmd.endOptionBlock(); - // create override attribute dataset (copied from findscu code) - if (cmd.findOption("--key", 0, OFCommandLine::FOM_FirstFromLeft)) - { - const char *ovKey = NULL; - do + // initialize default for --series-from + if (seriesFile_.length() > 0 && readSeriesInfo_) increment_ = OFTrue; + + cmd.beginOptionBlock(); + if (cmd.findOption("--instance-one")) { - app.checkValue(cmd.getValue(ovKey)); - overrideKeys.push_back(ovKey); - } while (cmd.findOption("--key", 0, OFCommandLine::FOM_NextFromLeft)); - } - DcmEncapsulatedDocument::setOverrideKeys(overrideKeys); - // initialize default for --series-from - if (opt_seriesFile != "" && opt_readSeriesInfo) opt_increment = OFTrue; + app.checkConflict("--instance-one", "--series-from", (seriesFile_.length() > 0) && readSeriesInfo_); + increment_ = OFFalse; + instance_ = 1; + } + if (cmd.findOption("--instance-inc")) + { + app.checkDependence("--instance-inc", "--series-from", (seriesFile_.length() > 0) && readSeriesInfo_); + increment_ = OFTrue; + } + if (cmd.findOption("--instance-set")) + { + increment_ = OFFalse; + app.checkValue(cmd.getValueAndCheckMin(instance_, 1)); + } + cmd.endOptionBlock(); - cmd.beginOptionBlock(); - if (cmd.findOption("--instance-one")) - { - app.checkConflict("--instance-one", "--series-from", - (opt_seriesFile != "") && opt_readSeriesInfo); - opt_increment = OFFalse; - opt_instance = 1; - } - if (cmd.findOption("--instance-inc")) - { - app.checkDependence("--instance-inc", "--series-from", - (opt_seriesFile != "") && opt_readSeriesInfo); - opt_increment = OFTrue; - } - if (cmd.findOption("--instance-set")) - { - opt_increment = OFFalse; - app.checkValue(cmd.getValueAndCheckMin(opt_instance, 1)); - } - cmd.endOptionBlock(); -} + cmd.beginOptionBlock(); + if (cmd.findOption("--annotation-yes")) + { + annotation_ = OFTrue; + } + if (cmd.findOption("--annotation-no")) + { + annotation_ = OFFalse; + } + cmd.endOptionBlock(); -OFCondition DcmEncapsulatedDocument::createIdentifiers(OFLogger &appLogger) -{ - char buf[100]; - OFCondition cond = EC_Normal; - Sint32 incrementedInstance = 0; - if (opt_seriesFile != "") - { - DcmFileFormat dfile; - cond = dfile.loadFile(opt_seriesFile, EXS_Unknown, EGL_noChange); - if (cond.bad()) + // ---------- parse CDA processing options ---------- + cmd.beginOptionBlock(); + if (cmd.findOption("--override")) { - OFLOG_WARN(appLogger, cond.text() - << ": reading file: " << opt_seriesFile); + app.checkConflict("--override", "--filetype-pdf", ftype_ == DT_pdfDocument); + app.checkConflict("--override", "--filetype-stl", ftype_ == DT_stlDocument); + app.checkConflict("--override", "--filetype-stl", ftype_ == DT_mtlDocument); + app.checkConflict("--override", "--filetype-stl", ftype_ == DT_objDocument); + override_ = OFTrue; } - else + if (cmd.findOption("--no-override")) { - const char *c = NULL; + app.checkConflict("--no-override", "--filetype-pdf", ftype_ == DT_pdfDocument); + app.checkConflict("--no-override", "--filetype-stl", ftype_ == DT_stlDocument); + app.checkConflict("--no-override", "--filetype-stl", ftype_ == DT_mtlDocument); + app.checkConflict("--no-override", "--filetype-stl", ftype_ == DT_objDocument); + override_ = OFFalse; + } + cmd.endOptionBlock(); + + // ---------- parse device data options ---------- + + if (cmd.findOption("--manufacturer")) + { + app.checkValue(cmd.getValue(manufacturer_)); + } + if (cmd.findOption("--manufacturer-model")) + { + app.checkValue(cmd.getValue(manufacturerModelName_)); + } + if (cmd.findOption("--device-serial")) + { + app.checkValue(cmd.getValue(deviceSerialNumber_)); + } + if (cmd.findOption("--software-versions")) + { + app.checkValue(cmd.getValue(softwareVersions_)); + } + + // ---------- parse STL processing options ---------- + + if (cmd.findOption("--measurement-units")) + { + app.checkConflict("--measurement-units", "--filetype-cda", ftype_ == DT_cdaDocument); + app.checkConflict("--measurement-units", "--filetype-pdf", ftype_ == DT_pdfDocument); + app.checkValue(cmd.getValue(measurementUnitsCSD_)); + app.checkValue(cmd.getValue(measurementUnitsCV_)); + app.checkValue(cmd.getValue(measurementUnitsCM_)); + } + + // ---------- parse input file format options ---------- + + cmd.beginOptionBlock(); + if (cmd.findOption("--write-xfer-little")) oxfer_ = EXS_LittleEndianExplicit; + if (cmd.findOption("--write-xfer-big")) oxfer_ = EXS_BigEndianExplicit; + if (cmd.findOption("--write-xfer-implicit")) oxfer_ = EXS_LittleEndianImplicit; + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--group-length-remove")) oglenc_ = EGL_withoutGL; + if (cmd.findOption("--group-length-create")) oglenc_ = EGL_withGL; + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--length-explicit")) oenctype_ = EET_ExplicitLength; + if (cmd.findOption("--length-undefined")) oenctype_ = EET_UndefinedLength; + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--padding-retain")) + { + app.checkConflict("--padding-retain", "--write-dataset", + writeMode_ == EWM_dataset); + opadenc_ = EPD_noChange; + } + if (cmd.findOption("--padding-off")) opadenc_ = EPD_withoutPadding; + if (cmd.findOption("--padding-create")) + { + app.checkConflict("--padding-create", "--write-dataset", + writeMode_ == EWM_dataset); + app.checkValue(cmd.getValueAndCheckMin(filepad_, 0)); + app.checkValue(cmd.getValueAndCheckMin(itempad_, 0)); + opadenc_ = EPD_withPadding; + } + cmd.endOptionBlock(); + + // create override attribute dataset + overrideKeys_.clear(); + if (cmd.findOption("--key", 0, OFCommandLine::FOM_FirstFromLeft)) + { + const char *ovKey = NULL; + do + { + app.checkValue(cmd.getValue(ovKey)); + overrideKeys_.push_back(ovKey); + } while (cmd.findOption("--key", 0, OFCommandLine::FOM_NextFromLeft)); + } + +} + + +OFCondition DcmEncapsulatedDocument::createIdentifiers() +{ + char buf[100]; + OFCondition cond = EC_Normal; + Sint32 incrementedInstance = 0; + if (seriesFile_.length() > 0) + { + DCMDATA_DEBUG("Reading series file"); + DcmFileFormat dfile; + cond = dfile.loadFile(seriesFile_, EXS_Unknown, EGL_noChange); + + if (cond.good()) + { + const char *c = NULL; DcmDataset *dset = dfile.getDataset(); if (dset) { - OFLOG_TRACE(appLogger, "reading patient attributes"); + DCMDATA_TRACE("reading patient attributes"); c = NULL; if (dset->findAndGetString(DCM_PatientName, c).good() && c) { - opt_patientName = c; + patientName_ = c; } c = NULL; if (dset->findAndGetString(DCM_PatientID, c).good() && c) { - opt_patientID = c; + patientID_ = c; } c = NULL; if (dset->findAndGetString(DCM_PatientBirthDate, c).good() && c) { - opt_patientBirthdate = c; + patientBirthdate_ = c; } c = NULL; if (dset->findAndGetString(DCM_PatientSex, c).good() && c) { - opt_patientSex = c; + patientSex_ = c; } - OFLOG_TRACE(appLogger, "reading study attributes"); + DCMDATA_TRACE("reading study attributes"); c = NULL; if (dset->findAndGetString(DCM_StudyInstanceUID, c).good() && c) { - opt_studyUID = c; + studyUID_ = c; + } + c = NULL; + if (dset->findAndGetString(DCM_SpecificCharacterSet, c).good() && c) + { + specificCharSet_ = c; } - OFLOG_TRACE(appLogger, "reading series attributes"); - if (opt_readSeriesInfo) + DCMDATA_TRACE("reading series attributes"); + if (readSeriesInfo_) { c = NULL; if (dset->findAndGetString(DCM_SeriesInstanceUID, c).good() && c) { - opt_seriesUID = c; + seriesUID_ = c; + } + c = NULL; + if (dset->findAndGetString(DCM_Modality, c).good() && c) + { + modality_ = c; } if (dset->findAndGetSint32(DCM_InstanceNumber, incrementedInstance).good()) @@ -854,627 +818,807 @@ OFCondition DcmEncapsulatedDocument::createIdentifiers(OFLogger &appLogger) { incrementedInstance = 0; } - if (opt_increment) opt_instance = incrementedInstance; + if (increment_) instance_ = incrementedInstance; + } + + DCMDATA_TRACE("reading reading device attributes"); + c = NULL; + if (dset->findAndGetString(DCM_Manufacturer, c).good() && c) + { + manufacturer_ = c; + } + c = NULL; + if (dset->findAndGetString(DCM_ManufacturerModelName, c).good() && c) + { + manufacturerModelName_ = c; + } + c = NULL; + if (dset->findAndGetString(DCM_DeviceSerialNumber, c).good() && c) + { + deviceSerialNumber_ = c; + } + c = NULL; + if (dset->findAndGetString(DCM_SoftwareVersions, c).good() && c) + { + softwareVersions_ = c; } - if (ftype == "stl") + + if (ftype_ == DT_stlDocument || ftype_ == DT_mtlDocument || ftype_ == DT_objDocument) { - OFLOG_TRACE(appLogger, "reading STL specific information"); + DCMDATA_TRACE("reading STL/MTL/OBJ specific information"); c = NULL; - OFLOG_TRACE(appLogger, "reading Frame of Reference Info"); if (dset->findAndGetString(DCM_FrameOfReferenceUID, c).good() && c) { - opt_frameOfReferenceUID = c; + frameOfReferenceUID_ = c; } c = NULL; if (dset->findAndGetString(DCM_PositionReferenceIndicator, c).good() && c) { - opt_positionReferenceIndicator = c; - } - OFLOG_TRACE(appLogger, "reading Enhanced Equipment info"); - c = NULL; - if (dset->findAndGetString(DCM_Manufacturer, c).good() && c) - { - opt_manufacturer = c; - } - c = NULL; - if (dset->findAndGetString(DCM_ManufacturerModelName, c).good() && c) - { - opt_manufacturerModelName = c; - } - c = NULL; - if (dset->findAndGetString(DCM_DeviceSerialNumber, c).good() && c) - { - opt_deviceSerialNumber = c; + positionReferenceIndicator_ = c; } - c = NULL; - if (dset->findAndGetString(DCM_SoftwareVersions, c).good() && c) - { - opt_softwareVersions = c; - } - OFLOG_TRACE(appLogger, "reading manufacturing 3d model info"); - { - OFLOG_TRACE(appLogger, "manufacturing 3d model info read successfully"); - } } } } + else + { + DCMDATA_ERROR("Error reading series file '" << seriesFile_ << "': " << cond.text()); + return cond; + } } - if (opt_studyUID.empty()) + + if (studyUID_.empty()) { dcmGenerateUniqueIdentifier(buf, SITE_STUDY_UID_ROOT); - opt_studyUID = buf; + studyUID_ = buf; } - if (opt_seriesUID.empty()) + if (seriesUID_.empty()) { dcmGenerateUniqueIdentifier(buf, SITE_SERIES_UID_ROOT); - opt_seriesUID = buf; + seriesUID_ = buf; } + return cond; } -int DcmEncapsulatedDocument::insertEncapsulatedDocument( - DcmItem *dataset, - OFLogger &appLogger) + +OFCondition DcmEncapsulatedDocument::formatSpecificProcessing() { - char buf[100]; - size_t fileSize = 0; - size_t buflen = 100; - struct stat fileStat; + if (ftype_ == DT_cdaDocument) + { + // so far we only have format specific processing code for CDA + return getCDAData(); + } + return EC_Normal; +} - if (0 == stat(opt_ifname.c_str(), &fileStat)) - { - fileSize = OFstatic_cast(size_t, fileStat.st_size); - } - else - { - OFLOG_ERROR(appLogger, "file " << opt_ifname << " not found"); - return EXITCODE_NO_INPUT_FILES; - } - if (fileSize == 0) - { - OFLOG_ERROR(appLogger, "file " << opt_ifname << " is empty"); - return EXITCODE_INVALID_INPUT_FILE; - } - FILE *encapfile = fopen(opt_ifname.c_str(), "rb"); - if (encapfile == NULL) - { - OFLOG_ERROR(appLogger, "unable to read file " << opt_ifname); - return EXITCODE_CANNOT_READ_INPUT_FILE; - } - if (fileSize < buflen) - { - buflen = fileSize; - } - if (buflen != fread(buf, 1, buflen, encapfile)) - { - OFLOG_ERROR(appLogger, "read error in file " << opt_ifname); - fclose(encapfile); - return EXITCODE_INVALID_INPUT_FILE; - } - if (ftype == "pdf") - { - // check magic word for PDF file - if (0 != strncmp("%PDF-", buf, 5)) +static size_t skipWhitespaceAndCommentLines(const char *buf, size_t buflen) +{ + size_t i=0; + while (OFTrue) { - OFLOG_ERROR(appLogger, "file " << opt_ifname << " is not a PDF file"); - fclose(encapfile); - return EXITCODE_INVALID_INPUT_FILE; + // skip whitespace at beginning of line + while (i < buflen && (buf[i]==' ' || buf[i]=='\t')) ++i; + + // skip comment line + if ((i < buflen) && (buf[i] == '#')) + { + // skip to the end of the comment line + while (i < buflen && (buf[i] != '\r' && buf[i] != '\n')) ++i; + } + else if (i < buflen && (buf[i]=='\r' || buf[i]=='\n')) + { + // skip empty line + ++i; + } + else return i; // end of buffer or keyword found } - // check PDF version number - char *version = buf + 5; - OFBool found = OFFalse; - for (int i = 0; i < 5; ++i) +} + +static OFString getNextKeyword(const char *buf) +{ + size_t i=0; + char c = buf[i]; + while ((c != ' ') && (c != '\t') && (c != '\0') && (c != '\r') && (c != '\n')) { - if (version[i] == 10 || version[i] == 13) - { - version[i] = 0; // insert end of string - found = OFTrue; - break; - } + ++i; + c = buf[i]; + } + return OFString(buf, i); +} + +static const char *objKeywords[] = { + "bevel", "bmat", "c_interp", "con", "cstype", "ctech", "curv", "curv2", + "d_interp", "deg", "end", "f", "g", "hole", "l", "lod", "mg", "mtllib", + "o", "p", "parm", "s", "scrv", "shadow_obj", "sp", "stech", "step", + "surf", "trace_obj", "trim", "usemtl", "v", "vn", "vp", "vt" +}; + + +OFCondition DcmEncapsulatedDocument::insertEncapsulatedDocument() +{ +#define INSERTBUFFERLENGTH 4096 + char buf[INSERTBUFFERLENGTH+1]; + size_t fileSize = 0; + size_t buflen = INSERTBUFFERLENGTH; + struct stat fileStat; + DcmDataset *dataset = dfile_.getDataset(); + + // determine length of document file + if (0 == stat(ifname_.c_str(), &fileStat)) + { + fileSize = OFstatic_cast(size_t, fileStat.st_size); + } + else + { + DCMDATA_ERROR("file " << ifname_ << " not found"); + return EC_InvalidStream; + } + if (fileSize == 0) + { + DCMDATA_ERROR("file " << ifname_ << " is empty"); + return EC_InvalidStream; + } + + // read the first 4096 bytes (or the full document if shorter) + FILE *encapfile = fopen(ifname_.c_str(), "rb"); + if (encapfile == NULL) + { + DCMDATA_ERROR("unable to read file " << ifname_); + return EC_InvalidStream; } - if (!found) + if (fileSize < buflen) { - OFLOG_ERROR(appLogger, "file " << opt_ifname - << ": unable to decode PDF version number"); + buflen = fileSize; + } + if (buflen != fread(buf, 1, buflen, encapfile)) + { + DCMDATA_ERROR("read error in file " << ifname_); fclose(encapfile); - return EXITCODE_INVALID_INPUT_FILE; + return EC_InvalidStream; } - OFLOG_INFO(appLogger, "file " << opt_ifname - << ": PDF " << version << ", " - << (fileSize + 1023) / 1024 << "kB"); - } - else - { - if (ftype == "cda") + + // null terminate buffer + buf[buflen] = '\0'; + + // if the user has not specified the file type, determine it from the file content + + // check for PDF document type + if (ftype_ == DT_unknownDocument) { - //xml validation occurs when getting data - OFLOG_INFO(appLogger, "file " << opt_ifname - << ": HL7 CDA file (XML Format)" << ", " - << (fileSize + 1023) / 1024 << "kB"); + // If the document starts with "%PDF-", it is a PDF document + if ((buflen > 5) && (0 == strncmp("%PDF-", buf, 5))) ftype_ = DT_pdfDocument; } - else + + // check for CDA document type + if (ftype_ == DT_unknownDocument) { - if (ftype == "stl") - { - // Each facet contains: - // - Normals: 3 floats (4 bytes) - // - Vertices: 3x floats (4 byte each, 12 bytes total) - // - AttributeCount: 1 short (2 bytes) - // Total: 50 bytes per facet - const size_t facetSize32 = 3 * sizeof(Float32) - + 3 * 3 * sizeof(Float32) - + sizeof(Uint16); - const size_t facetSize64 = 3 * sizeof(Float64) - + 3 * 3 * sizeof(Float64) - + sizeof(Uint16); - // STL validation for ASCII CODE - if (fileSize < 15) + size_t i = 0; + // in a CDA document, the first non-whitespace character is "<" + while (i < buflen && OFStandard::isspace(buf[i])) ++i; + // we also check for the presence of the keyword "ClinicalDocument" + if ((i < buflen && buf[i] == '<') && (NULL != strstr(buf, "ClinicalDocument"))) ftype_ = DT_cdaDocument; + } + + // check for binary STL document type + if (ftype_ == DT_unknownDocument) + { + // See comments in the STL section below for explanation of the identification algorithm + if (fileSize >= 84) { - // "solid " and "endsolid " markers for an ASCII file - OFLOG_ERROR(appLogger, "The STL file is not long enough" - << " (" << fileSize << "kB)"); - fclose(encapfile); - return EXITCODE_INVALID_INPUT_FILE; + const size_t facetSize32 = 12 * sizeof(Float32) + sizeof(Uint16); + const size_t facetSize64 = 12 * sizeof(Float64) + sizeof(Uint16); + + // the number of triangles is stored as a 32-bit little endian integer at byte offset 80 in the file + Uint32 nTriangles = 0; + for (int j = 3; j >= 0; --j) + { + nTriangles = (nTriangles << 8) + OFstatic_cast(unsigned char, buf[80 + j]); + } + if (fileSize == (84 + nTriangles * facetSize32) || fileSize == (84 + nTriangles * facetSize64)) ftype_ = DT_stlDocument; } - // Binary files should never start with "solid ", - // but just in case, check for ASCII, - // and if not valid then check for binary... - // Look for text "solid " in first 6 bytes, - // indicating the possibility that this is an - // ASCII STL format. - if (0 == strncmp("solid ", buf, 6)) + } + + // check for MTL document type + if (ftype_ == DT_unknownDocument) + { + size_t i = skipWhitespaceAndCommentLines(buf, buflen); + if (i < buflen) { - OFLOG_ERROR(appLogger, "File " << opt_ifname - << " starts with 'solid '. " - << "It is a valid STL file but it is in ASCII Code" - << "and DICOM only accepts binary STL"); - return EXITCODE_INVALID_INPUT_FILE; + OFString keyword = getNextKeyword(buf+i); + if (keyword == "newmtl") ftype_ = DT_mtlDocument; } - //////STL validation for Binary Format - else + } + + // check for OBJ document type + if (ftype_ == DT_unknownDocument) + { + size_t i = skipWhitespaceAndCommentLines(buf, buflen); + if (i < buflen) { - OFLOG_DEBUG(appLogger, "Magic word 'solid ' not found. " - << "Validating STL file " - << "in Binary format"); - // 80-byte header + 4-byte "number of triangles" for a binary file - if (fileSize < 84) + OFString keyword = getNextKeyword(buf+i); + size_t numKeywords = sizeof(objKeywords)/sizeof(const char *); + for (size_t j=0; j < numKeywords; ++j) { - OFLOG_ERROR(appLogger, "The binary STL file is not long enough" - << " (" << fileSize << "kB)"); - fclose(encapfile); - return EXITCODE_INVALID_INPUT_FILE; + if (keyword == objKeywords[j]) + { + ftype_ = DT_objDocument; + break; + } } - // Header is from bytes 0-79 - // The number of Triangles starts at byte offset 80 and is a uint32 (4 Bytes) - char ntriangleschar[5]; - for (int j = 0; j < 4; j++) - ntriangleschar[j] = buf[80 + j]; - ntriangleschar[4] = 0; - Uint32 *nTriangles = OFreinterpret_cast(Uint32*, ntriangleschar); - // Verify that file size equals the sum of - // header + nTriangles value + all triangles - OFLOG_DEBUG(appLogger, "verifying if the file size is consistent"); - if (fileSize == (84 + *OFconst_cast(Uint32*, nTriangles) * facetSize32) || - fileSize == (84 + *OFconst_cast(Uint32*, nTriangles) * facetSize64)) - { - OFLOG_DEBUG(appLogger, "File " << opt_ifname - << " passed binary STL validation." << OFendl - << "Assuming valid STL file " - << "in binary format" - ); - OFLOG_TRACE(appLogger, "The binary STL file is:" << OFendl - << fileSize << " kB " << " as expected." << OFendl - << (84 + *OFconst_cast(Uint32 * , nTriangles) * facetSize32) << " kB for x86" << OFendl - << (84 + *OFconst_cast(Uint32 * , nTriangles) * facetSize64) << " kB for x64" << OFendl - << "(84 + triangles number * facet size)" << OFendl - << " number of Triangles " << *OFconst_cast(Uint32 * , nTriangles) << OFendl - << " nTriangles (Uint32): " << nTriangles << OFendl - << " facetSize32: " << facetSize32 << OFendl - << " facetSize64: " << facetSize64 << OFendl - ); - } - else - { - OFLOG_ERROR(appLogger, "The binary STL file is not consistent." << OFendl - << (84 + *OFconst_cast(Uint32 * , nTriangles) * facetSize32) << " kB for x86 and " - << (84 + *OFconst_cast(Uint32 * , nTriangles) * facetSize64) << " kB for x64 " << OFendl - << "(84 + triangles number * facet size)" << OFendl - << " number of Triangles " << *OFconst_cast(Uint32 * , nTriangles) << OFendl - << " nTriangles (Uint32): " << nTriangles << OFendl - << " facetSize32: " << facetSize32 << OFendl - << " facetSize64: " << facetSize64 << OFendl - ); + } + } + + // check if we have determined the file type, bail out otherwise + if (ftype_ == DT_unknownDocument) + { + DCMDATA_ERROR("file " << ifname_ << " is of unknown file type"); + fclose(encapfile); + return EC_InvalidStream; + } + + OFBool found; + char *version; + + // Each STL facet contains: + // - Normals: 3 floats (4 bytes) + // - Vertices: 3x floats (4 byte each, 12 bytes total) + // - AttributeCount: 1 short (2 bytes) + // Total: 50 bytes per facet + const size_t facetSize32 = 12 * sizeof(Float32) + sizeof(Uint16); + const size_t facetSize64 = 12 * sizeof(Float64) + sizeof(Uint16); + size_t i; + + switch (ftype_) + { + case DT_pdfDocument: // special handling for PDF documents + // check magic word for PDF file + if (0 != strncmp("%PDF-", buf, 5)) + { + DCMDATA_ERROR("file " << ifname_ << " is not a PDF file"); + fclose(encapfile); + return EC_InvalidStream; + } + // check PDF version number + version = buf + 5; + found = OFFalse; + for (i = 0; i < 5; ++i) + { + if (version[i] == 10 || version[i] == 13) + { + version[i] = 0; // insert end of string + found = OFTrue; + break; + } + } + if (!found) + { + DCMDATA_ERROR("file " << ifname_ << ": unable to decode PDF version number"); + fclose(encapfile); + return EC_InvalidStream; + } + DCMDATA_INFO("file " << ifname_ << ": PDF " << version << ", " << (fileSize + 1023) / 1024 << "kB"); + break; + + case DT_cdaDocument: // special handling for CDA documents + // proper xml validation occurs later + DCMDATA_INFO("file " << ifname_ << ": HL7 CDA file (XML Format)" << ", " << (fileSize + 1023) / 1024 << "kB"); + break; + + case DT_stlDocument: // special handling for STL documents + // Check for an ASCII STL document (which is not permitted in DICOM) + if (fileSize < 15) + { + // "solid " and "endsolid " markers for an ASCII file + DCMDATA_ERROR("The STL file is not long enough" << " (" << fileSize << "kB)"); + fclose(encapfile); + return EC_InvalidStream; + } + + // Binary STL files should never start with "solid ", but just in case, check for ASCII, + // and if not valid then check for binary... Look for text "solid " in first 6 bytes, + // indicating the possibility that this is an ASCII STL format. + if (0 == strncmp("solid ", buf, 6)) + { + DCMDATA_ERROR("File " << ifname_ << " starts with 'solid '. It is an STL file but it is in ASCII and DICOM only permits binary STL"); + return EC_InvalidStream; + } + else + { + // STL validation for Binary Format + DCMDATA_DEBUG("Validating binary STL file"); + // 80-byte header + 4-byte "number of triangles" for a binary file + if (fileSize < 84) + { + DCMDATA_ERROR("The binary STL file is not long enough" << " (" << fileSize << "kB)"); + fclose(encapfile); + return EC_InvalidStream; + } + // Header is from bytes 0-79 + // The number of Triangles starts at byte offset 80 and is a + // unsigned 32-bit integer in little endian byte order + Uint32 nTriangles = 0; + for (int j = 3; j >= 0; --j) + nTriangles = (nTriangles << 8) + OFstatic_cast(unsigned char, buf[80 + j]); + + // Verify that file size equals the sum of + // header + nTriangles value + all triangles + DCMDATA_DEBUG("verifying if the file size is consistent"); + if (fileSize == (84 + nTriangles * facetSize32) || + fileSize == (84 + nTriangles * facetSize64)) + { + DCMDATA_INFO("file " << ifname_ << ": binary STL, " << nTriangles << " triangles"); + DCMDATA_DEBUG("The binary STL file is " + << fileSize << " bytes (expected " + << (84 + nTriangles * facetSize32) << " bytes for x86 or " + << (84 + nTriangles * facetSize64) << " bytes for x64), number of triangles: " << nTriangles); + } + else + { + DCMDATA_ERROR("The binary STL file is not consistent. Expected " + << (84 + nTriangles * facetSize32) << " bytes for x86 or " + << (84 + nTriangles * facetSize64) << " bytes for x64"); + fclose(encapfile); + return EC_InvalidStream; + } + } + break; + + case DT_mtlDocument: // special handling for MTL documents + i = skipWhitespaceAndCommentLines(buf, buflen); + if (i < buflen) + { + OFString keyword = getNextKeyword(buf+i); + if (keyword != "newmtl") + { + DCMDATA_ERROR("Not a valid MTL file. Expected 'newmtl' as first keyword, but found '" << keyword << "'"); + fclose(encapfile); + return EC_InvalidStream; + } + } + else + { + DCMDATA_ERROR("Not a valid MTL file. No keyword found"); + fclose(encapfile); + return EC_InvalidStream; + } + DCMDATA_INFO("file " << ifname_ << ": Wavefront MTL"); + break; + + case DT_objDocument: // special handling for OBJ documents + i = skipWhitespaceAndCommentLines(buf, buflen); + if (i < buflen) + { + OFString keyword = getNextKeyword(buf+i); + size_t numKeywords = sizeof(objKeywords)/sizeof(const char *); + found = OFFalse; + for (size_t j=0; j < numKeywords; ++j) + { + if (keyword == objKeywords[j]) + { + found = OFTrue; + break; + } + } + if (!found) + { + DCMDATA_ERROR("Not a valid OBJ file. Unsupported keyword '" << keyword << "' found"); + fclose(encapfile); + return EC_InvalidStream; + } + } + else + { + DCMDATA_ERROR("Not a valid OBJ file. No keyword found"); + fclose(encapfile); + return EC_InvalidStream; + } + DCMDATA_INFO("file " << ifname_ << ": Wavefront OBJ"); + break; + + case DT_unknownDocument: + DCMDATA_ERROR("Unsupported filetype. This should not happen."); fclose(encapfile); - return EXITCODE_INVALID_INPUT_FILE; - } + return EC_InvalidStream; + break; + } + + // seek to start of file + if (0 != fseek(encapfile, 0, SEEK_SET)) + { + DCMDATA_ERROR("file " << ifname_ << ": seek error"); + fclose(encapfile); + return EC_InvalidStream; + } + + OFCondition result = EC_Normal; + DcmPolymorphOBOW *elem = new DcmPolymorphOBOW(DCM_EncapsulatedDocument); + if (elem) + { + size_t numBytes = fileSize; + + // Store Encapsulated Document Length, according to CP 1851 + result = dataset->putAndInsertUint32(DCM_EncapsulatedDocumentLength, OFstatic_cast(Uint32, numBytes)); + + // allocate an even number of bytes + if (numBytes & 1) ++numBytes; + Uint8 *bytes = NULL; + if (result.good()) + { + result = elem->createUint8Array(OFstatic_cast(Uint32, numBytes), bytes); + } + else + { + return result; + } + + // read file content into Encapsulated Document attribute + if (result.good()) + { + // blank pad byte + bytes[numBytes - 1] = 0; + // read file content + if (fileSize != fread(bytes, 1, fileSize, encapfile)) + { + DCMDATA_ERROR("read error in file " << ifname_); + return result; } } else { - OFLOG_WARN(appLogger, "Filetype not supported or filetype not set. Current ftype is " << ftype << OFendl - << "The name of the passed logger is: " << appLogger.getName()); + return EC_MemoryExhausted; } } - } - if (0 != fseek(encapfile, 0, SEEK_SET)) - { - OFLOG_ERROR(appLogger, "file " << opt_ifname << ": seek error"); - fclose(encapfile); - return EXITCODE_CANNOT_READ_INPUT_FILE; - } - OFCondition result = EC_Normal; - DcmPolymorphOBOW *elem = new DcmPolymorphOBOW(DCM_EncapsulatedDocument); - if (elem) - { - size_t numBytes = fileSize; - // according to CP 1851 - result = dataset->putAndInsertUint32(DCM_EncapsulatedDocumentLength, OFstatic_cast(Uint32, numBytes)); - if (numBytes & 1) ++numBytes; - Uint8 *bytes = NULL; - if (result.good()) - { - result = elem->createUint8Array(OFstatic_cast(Uint32, numBytes), bytes); - } else { - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; + fclose(encapfile); + return EC_MemoryExhausted; } + // if successful, insert Encapsulated Document element into dataset if (result.good()) { - // blank pad byte - bytes[numBytes - 1] = 0; - // read file content - if (fileSize != fread(bytes, 1, fileSize, encapfile)) - { - OFLOG_ERROR(appLogger, "read error in file " << opt_ifname); - return EXITCODE_CANNOT_READ_INPUT_FILE; - } + result = dataset->insert(elem); } else { - return EXITCODE_MEMORY_EXHAUSTED; + delete elem; + DCMDATA_ERROR("Unsuccessful, did not insert element."); + return result; } - } - else - { + + // close file end return fclose(encapfile); - return EXITCODE_MEMORY_EXHAUSTED; - } - // if successful, insert element into dataset - if (result.good()) - { - result = dataset->insert(elem); - } - else - { - delete elem; - OFLOG_ERROR(appLogger, "Unsuccessful, did not insert element."); - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } - // close file - fclose(encapfile); - if (result.good()) - { - return EXITCODE_NO_ERROR; - } - else - { - return EXITCODE_CANNOT_WRITE_OUTPUT_FILE; - } + return result; } -OFCondition DcmEncapsulatedDocument::createHeader( - DcmItem *dataset, - OFLogger &logger) +OFCondition DcmEncapsulatedDocument::addFrameOfReferenceModule(DcmItem *dataset) { - OFCondition result = EC_Normal; - char buf[80]; - // insert empty type 2 attributes - if (result.good()) result = dataset->insertEmptyElement(DCM_StudyDate); - if (result.good()) result = dataset->insertEmptyElement(DCM_StudyTime); - if (result.good()) result = dataset->insertEmptyElement(DCM_AccessionNumber); - if (result.good()) result = dataset->insertEmptyElement(DCM_Manufacturer); - if (result.good()) result = dataset->insertEmptyElement(DCM_ReferringPhysicianName); - if (result.good()) result = dataset->insertEmptyElement(DCM_StudyID); - if (result.good()) result = dataset->insertEmptyElement(DCM_ContentDate); - if (result.good()) result = dataset->insertEmptyElement(DCM_ContentTime); - if (result.good()) result = dataset->insertEmptyElement(DCM_AcquisitionDateTime); - if (result.good()) - { - if (opt_conceptCSD != "" && opt_conceptCV != "" && opt_conceptCM != "") + DCMDATA_TRACE("Validating Frame of Reference UID value"); + char buf [100]; + if (frameOfReferenceUID_.empty()) { - result = DcmCodec::insertCodeSequence(dataset, DCM_ConceptNameCodeSequence, - opt_conceptCSD.c_str(), - opt_conceptCV.c_str(), - opt_conceptCM.c_str()); + DCMDATA_DEBUG("Frame of Reference UID " << DCM_FrameOfReferenceUID << " value missing, generating a new one." + ); + dcmGenerateUniqueIdentifier(buf, SITE_SERIES_UID_ROOT); + frameOfReferenceUID_ = buf; } else { - result = dataset->insertEmptyElement(DCM_ConceptNameCodeSequence); + if (DcmUniqueIdentifier::checkStringValue(frameOfReferenceUID_, "1").bad()) + { + DCMDATA_DEBUG("Frame of Reference UID " << DCM_FrameOfReferenceUID << " value was faulty, generating a new one." + ); + dcmGenerateUniqueIdentifier(buf, SITE_SERIES_UID_ROOT); + frameOfReferenceUID_ = buf; + } } - } - // insert const value attributes - if (result.good()) - { - result = dataset->putAndInsertString(DCM_SpecificCharacterSet, "ISO_IR 100"); - } - //insert encapsulated file storage UID (CDA/PDF/STL) - if (result.good()) - { - if (ftype == "pdf") + DCMDATA_TRACE("Inserting Frame of Reference info to dataset"); + OFCondition result = dataset->putAndInsertOFStringArray(DCM_FrameOfReferenceUID, frameOfReferenceUID_); + + if (result.good()) + result = dataset->putAndInsertOFStringArray(DCM_PositionReferenceIndicator, positionReferenceIndicator_); + + return result; +} + + +OFCondition DcmEncapsulatedDocument::addEnhancedGeneralEquipmentModule(DcmItem *dataset) +{ + DCMDATA_TRACE("Validating and inserting Enhanced General Equipment fields"); + if (manufacturer_.empty()) { - OFLOG_TRACE(logger, "Inserting SOPClassUID to dataset"); - result = dataset->putAndInsertString(DCM_SOPClassUID, UID_EncapsulatedPDFStorage); + manufacturer_ = "DCMTK_MANUFACTURING"; + DCMDATA_INFO("No Manufacturer " << DCM_Manufacturer << " specified, will use dummy value."); } - if (ftype == "cda") + OFCondition result = dataset->putAndInsertOFStringArray(DCM_Manufacturer, manufacturer_); + if (result.good()) { - OFLOG_TRACE(logger, "Inserting SOPClassUID to dataset"); - result = dataset->putAndInsertString(DCM_SOPClassUID, UID_EncapsulatedCDAStorage); - } - if (ftype == "stl") - {//STL Specific modules - OFLOG_TRACE(logger, "Validating Frame of Reference UID value"); - if (opt_frameOfReferenceUID.empty()) - { - OFLOG_DEBUG(logger, "Frame of Reference UID " - << DCM_FrameOfReferenceUID - << " value was empty, generating a new one." - ); - dcmGenerateUniqueIdentifier(buf, SITE_SERIES_UID_ROOT); - opt_frameOfReferenceUID = buf; - } - else - { - if (DcmUniqueIdentifier::checkStringValue(opt_frameOfReferenceUID, "1").bad()) - { - OFLOG_DEBUG(logger, "Frame of Reference UID " - << DCM_FrameOfReferenceUID - << " value was faulty, generating a new one." - ); - dcmGenerateUniqueIdentifier(buf, SITE_SERIES_UID_ROOT); - opt_frameOfReferenceUID = buf; - } - } - if (result.good()) - { - OFLOG_TRACE(logger, "Inserting Frame of Reference info to dataset"); - result = dataset->putAndInsertOFStringArray(DCM_FrameOfReferenceUID, opt_frameOfReferenceUID); - } - if (result.good()) - result = dataset->putAndInsertOFStringArray(DCM_PositionReferenceIndicator, opt_positionReferenceIndicator); - OFLOG_TRACE(logger, "Validating and inserting Enhanced General Equipment fields"); - if (result.good()) - { - if (opt_manufacturer.empty()) + if (manufacturerModelName_.empty()) { - opt_manufacturer = "DCMTK_MANUFACTURING"; - OFLOG_WARN(logger, "No Manufacturer " - << DCM_Manufacturer - << " provided nor found in series. This attribute is " - << "required for Enhanced General Equipment module. " - << opt_manufacturer - << " will be inserted as dummy value." - ); + manufacturerModelName_ = "DCMTK_3DMODEL"; + DCMDATA_INFO("No Manufacturer Model Name " << DCM_ManufacturerModelName << " specified, will use dummy value."); } - result = dataset->putAndInsertOFStringArray(DCM_Manufacturer, opt_manufacturer); - } - if (result.good()) - { - if (opt_manufacturerModelName.empty()) - { - opt_manufacturerModelName = "DCMTK_3DMODEL_3"; - OFLOG_WARN(logger, "No Manufacturer Model Name " - << DCM_ManufacturerModelName - << " provided nor found in series. This attribute is " - << "required for Enhanced General Equipment module. " - << opt_manufacturerModelName - << " will be inserted as dummy value." - ); - } - result = dataset->putAndInsertOFStringArray(DCM_ManufacturerModelName, opt_manufacturerModelName); - } - if (result.good()) - { - if (opt_deviceSerialNumber.empty()) - { - opt_deviceSerialNumber = "DCMTK01234567890"; - OFLOG_WARN(logger, "No Device Serial Number " - << DCM_DeviceSerialNumber - << " provided nor found in series. This attribute is " - << "required for Enhanced General Equipment module. " - << opt_deviceSerialNumber - << " will be inserted as dummy value." - ); - } - result = dataset->putAndInsertOFStringArray(DCM_DeviceSerialNumber, opt_deviceSerialNumber); - } - if (result.good()) - { - if (opt_softwareVersions.empty()) - { - opt_softwareVersions = OFFIS_DCMTK_VERSION; - OFLOG_WARN(logger, "No Software Versions " - << DCM_SoftwareVersions - << " provided nor found in series. This attribute is " - << "required for Enhanced General Equipment module. " - << opt_softwareVersions - << " will be inserted as dummy value." - ); - } - result = dataset->putAndInsertOFStringArray(DCM_SoftwareVersions, opt_softwareVersions); - } - if (result.good()) - { - if (opt_measurementUnitsCSD != "" && opt_measurementUnitsCV != "" && opt_measurementUnitsCM != "") + result = dataset->putAndInsertOFStringArray(DCM_ManufacturerModelName, manufacturerModelName_); + } + if (result.good()) + { + if (deviceSerialNumber_.empty()) { - result = DcmCodec::insertCodeSequence(dataset, DCM_MeasurementUnitsCodeSequence, - opt_measurementUnitsCSD.c_str(), - opt_measurementUnitsCV.c_str(), - opt_measurementUnitsCM.c_str()); + deviceSerialNumber_ = "DCMTK_1234567890"; + DCMDATA_INFO("No Device Serial Number " << DCM_DeviceSerialNumber << " specified, will use dummy value."); } - else + result = dataset->putAndInsertOFStringArray(DCM_DeviceSerialNumber, deviceSerialNumber_); + } + if (result.good()) + { + if (softwareVersions_.empty()) { - OFLOG_DEBUG(logger, "Measurement Units Code Sequence " - << DCM_FrameOfReferenceUID - << "had one or more empty values, generating default values." - ); - result = DcmCodec::insertCodeSequence(dataset, DCM_MeasurementUnitsCodeSequence, "UCUM", "um", "um"); + softwareVersions_ = OFFIS_DCMTK_VERSION; + DCMDATA_INFO("No Software Versions " << DCM_SoftwareVersions << " specified, will use dummy value."); } - } - if (result.good()) - { - OFLOG_TRACE(logger, "Inserting SOPClassUID to dataset"); - result = dataset->putAndInsertString(DCM_SOPClassUID, UID_EncapsulatedSTLStorage); - } + result = dataset->putAndInsertOFStringArray(DCM_SoftwareVersions, softwareVersions_); } - } - if (result.good()) - { - if (ftype == "stl") + return result; +} + + +OFCondition DcmEncapsulatedDocument::addManufacturing3DModelModule(DcmItem *dataset) +{ + OFCondition result = EC_Normal; + + if ((measurementUnitsCSD_.length() > 0) && (measurementUnitsCV_.length() > 0) && (measurementUnitsCM_.length() > 0)) { - result = dataset->putAndInsertString(DCM_Modality, "M3D"); + result = DcmCodec::insertCodeSequence(dataset, DCM_MeasurementUnitsCodeSequence, + measurementUnitsCSD_.c_str(), + measurementUnitsCV_.c_str(), + measurementUnitsCM_.c_str()); } else { - // we are now using "DOC" for the modality, which seems to be more appropriate than "OT" (see CP-749) - result = dataset->putAndInsertString(DCM_Modality, "DOC"); + DCMDATA_DEBUG("Measurement Units Code Sequence " + << DCM_MeasurementUnitsCodeSequence + << " had one or more empty values, generating default values." + ); + result = DcmCodec::insertCodeSequence(dataset, DCM_MeasurementUnitsCodeSequence, "UCUM", "um", "um"); } - } - if (result.good()) - { - if (ftype != "stl") + return result; +} + + +OFCondition DcmEncapsulatedDocument::createHeader() +{ + OFCondition result = EC_Normal; + DcmDataset *dataset = dfile_.getDataset(); + + char buf[80]; + // insert empty type 2 attributes + if (result.good()) result = dataset->insertEmptyElement(DCM_StudyDate); + if (result.good()) result = dataset->insertEmptyElement(DCM_StudyTime); + if (result.good()) result = dataset->insertEmptyElement(DCM_AccessionNumber); + if (result.good()) result = dataset->insertEmptyElement(DCM_ReferringPhysicianName); + if (result.good()) result = dataset->insertEmptyElement(DCM_StudyID); + if (result.good()) result = dataset->insertEmptyElement(DCM_ContentDate); + if (result.good()) result = dataset->insertEmptyElement(DCM_ContentTime); + if (result.good()) result = dataset->insertEmptyElement(DCM_AcquisitionDateTime); + if (result.good()) { - OFLOG_TRACE(logger, "Inserting default Conversion type: Workstation (WSD) to dataset"); - result = dataset->putAndInsertString(DCM_ConversionType, "WSD"); + if ((conceptCSD_.length() > 0) && (conceptCV_.length() > 0) && (conceptCM_.length() > 0)) + { + result = DcmCodec::insertCodeSequence(dataset, DCM_ConceptNameCodeSequence, + conceptCSD_.c_str(), + conceptCV_.c_str(), + conceptCM_.c_str()); + } + else + { + result = dataset->insertEmptyElement(DCM_ConceptNameCodeSequence); + } } - else + + // there is no way we could determine a meaningful series number, so we just use a constant. + if (result.good()) result = dataset->putAndInsertString(DCM_SeriesNumber, "1"); + + // insert variable value attributes + if (result.good()) result = dataset->putAndInsertString(DCM_DocumentTitle, documentTitle_.c_str()); + if (result.good()) result = dataset->putAndInsertString(DCM_PatientName, patientName_.c_str()); + if (result.good()) result = dataset->putAndInsertString(DCM_PatientID, patientID_.c_str()); + if (result.good()) result = dataset->putAndInsertString(DCM_PatientBirthDate, patientBirthdate_.c_str()); + if (result.good()) result = dataset->putAndInsertString(DCM_PatientSex, patientSex_.c_str()); + if (result.good()) result = dataset->putAndInsertString(DCM_BurnedInAnnotation, annotation_ ? "YES" : "NO"); + OFStandard::snprintf(buf, sizeof(buf), "%ld", OFstatic_cast(long, instance_)); + if (result.good()) result = dataset->putAndInsertString(DCM_InstanceNumber, buf); + dcmGenerateUniqueIdentifier(buf, SITE_INSTANCE_UID_ROOT); + if (result.good()) result = dataset->putAndInsertString(DCM_StudyInstanceUID, studyUID_.c_str()); + if (result.good()) result = dataset->putAndInsertString(DCM_SeriesInstanceUID, seriesUID_.c_str()); + if (result.good()) result = dataset->putAndInsertString(DCM_SOPInstanceUID, buf); + // set instance creation date and time + OFString s; + if (result.good()) result = DcmDate::getCurrentDate(s); + if (result.good()) result = dataset->putAndInsertOFStringArray(DCM_InstanceCreationDate, s); + if (result.good()) result = DcmTime::getCurrentTime(s); + if (result.good()) result = dataset->putAndInsertOFStringArray(DCM_InstanceCreationTime, s); + + // Device data. Special handling of these attributes for STL/MTL/OBJ, + // where they are type 1, will be provided later in addEnhancedGeneralEquipmentModule() + if (result.good() && (manufacturer_.length() > 0)) + result = dataset->putAndInsertOFStringArray(DCM_Manufacturer, manufacturer_.c_str()); + else result = dataset->insertEmptyElement(DCM_Manufacturer); + if (result.good() && (manufacturerModelName_.length() > 0)) result = dataset->putAndInsertOFStringArray(DCM_ManufacturerModelName, manufacturerModelName_.c_str()); + if (result.good() && (deviceSerialNumber_.length() > 0)) result = dataset->putAndInsertOFStringArray(DCM_DeviceSerialNumber, deviceSerialNumber_.c_str()); + if (result.good() && (softwareVersions_.length() > 0)) result = dataset->putAndInsertOFStringArray(DCM_SoftwareVersions, softwareVersions_.c_str()); + + //insert encapsulated file storage UID and additional attributes required per SOP class + if (result.good()) { - OFLOG_TRACE(logger, "STL has no Conversion Type"); - result = EC_Normal; + switch (ftype_) + { + case DT_pdfDocument: + DCMDATA_TRACE("Inserting SOPClassUID to dataset"); + result = dataset->putAndInsertString(DCM_SOPClassUID, UID_EncapsulatedPDFStorage); + if (result.good()) result = dataset->putAndInsertString(DCM_MIMETypeOfEncapsulatedDocument, "application/pdf"); + if (result.good()) + { + if ((modality_.length() > 0) && (modality_ != "DOC")) + { + DCMDATA_ERROR("Cannot use series information from '" << seriesFile_ << "': modality mismatch, expected 'DOC', found '" << modality_ << "'"); + result = EC_InvalidValue; + } + else result = dataset->putAndInsertString(DCM_Modality, "DOC"); + } + if (result.good()) result = dataset->putAndInsertString(DCM_ConversionType, "WSD"); + if (result.good() && (specificCharSet_.length() > 0)) result = dataset->putAndInsertString(DCM_SpecificCharacterSet, specificCharSet_.c_str()); + break; + + case DT_cdaDocument: + DCMDATA_TRACE("Inserting SOPClassUID to dataset"); + result = dataset->putAndInsertString(DCM_SOPClassUID, UID_EncapsulatedCDAStorage); + if (result.good()) result = dataset->putAndInsertString(DCM_MIMETypeOfEncapsulatedDocument, "text/XML"); + if (result.good()) + { + if ((modality_.length() > 0) && (modality_ != "DOC")) + { + DCMDATA_ERROR("Cannot use series information from '" << seriesFile_ << "': modality mismatch, expected 'DOC', found '" << modality_ << "'"); + result = EC_InvalidValue; + } + else result = dataset->putAndInsertString(DCM_Modality, "DOC"); + } + if (result.good()) result = dataset->putAndInsertString(DCM_ConversionType, "WSD"); + // Patient Name and Patient ID are guaranteed to be in UTF-8 (ISO_IR 192) in the CDA document + // and no other attributes from the series file are affected by character set issues + if ((! override_) || (specificCharSet_.length() == 0) ) specificCharSet_ = "ISO_IR 192"; + if (result.good()) result = dataset->putAndInsertString(DCM_SpecificCharacterSet, specificCharSet_.c_str()); + if (result.good() && cda_mediaTypes.length() > 0) + { + result = dataset->putAndInsertString(DCM_ListOfMIMETypes, cda_mediaTypes.c_str()); + } + if (result.good() && hl7_InstanceIdentifier.length() > 0) + { + result = dataset->putAndInsertString(DCM_HL7InstanceIdentifier, hl7_InstanceIdentifier.c_str()); + } + break; + + case DT_stlDocument: + DCMDATA_TRACE("Inserting SOPClassUID to dataset"); + result = dataset->putAndInsertString(DCM_SOPClassUID, UID_EncapsulatedSTLStorage); + if (result.good()) result = dataset->putAndInsertString(DCM_MIMETypeOfEncapsulatedDocument, "model/stl"); + if (result.good()) + { + if ((modality_.length() > 0) && (modality_ != "M3D")) + { + DCMDATA_ERROR("Cannot use series information from '" << seriesFile_ << "': modality mismatch, expected 'M3D', found '" << modality_ << "'"); + result = EC_InvalidValue; + } + else result = dataset->putAndInsertString(DCM_Modality, "M3D"); + } + if (result.good() && (specificCharSet_.length() > 0)) result = dataset->putAndInsertString(DCM_SpecificCharacterSet, specificCharSet_.c_str()); + if (result.good()) result = addFrameOfReferenceModule(dataset); + if (result.good()) result = addEnhancedGeneralEquipmentModule(dataset); + if (result.good()) result = addManufacturing3DModelModule(dataset); + break; + + case DT_mtlDocument: + DCMDATA_TRACE("Inserting SOPClassUID to dataset"); + result = dataset->putAndInsertString(DCM_SOPClassUID, UID_EncapsulatedMTLStorage); + if (result.good()) result = dataset->putAndInsertString(DCM_MIMETypeOfEncapsulatedDocument, "model/mtl"); + if (result.good()) + { + if ((modality_.length() > 0) && (modality_ != "M3D")) + { + DCMDATA_ERROR("Cannot use series information from '" << seriesFile_ << "': modality mismatch, expected 'M3D', found '" << modality_ << "'"); + result = EC_InvalidValue; + } + else result = dataset->putAndInsertString(DCM_Modality, "M3D"); + } + if (result.good() && (specificCharSet_.length() > 0)) result = dataset->putAndInsertString(DCM_SpecificCharacterSet, specificCharSet_.c_str()); + if (result.good()) result = addFrameOfReferenceModule(dataset); + if (result.good()) result = addEnhancedGeneralEquipmentModule(dataset); + if (result.good()) result = addManufacturing3DModelModule(dataset); + break; + + case DT_objDocument: + DCMDATA_TRACE("Inserting SOPClassUID to dataset"); + result = dataset->putAndInsertString(DCM_SOPClassUID, UID_EncapsulatedOBJStorage); + if (result.good()) result = dataset->putAndInsertString(DCM_MIMETypeOfEncapsulatedDocument, "model/obj"); + if (result.good()) + { + if ((modality_.length() > 0) && (modality_ != "M3D")) + { + DCMDATA_ERROR("Cannot use series information from '" << seriesFile_ << "': modality mismatch, expected 'M3D', found '" << modality_ << "'"); + result = EC_InvalidValue; + } + else result = dataset->putAndInsertString(DCM_Modality, "M3D"); + } + if (result.good() && (specificCharSet_.length() > 0)) result = dataset->putAndInsertString(DCM_SpecificCharacterSet, specificCharSet_.c_str()); + if (result.good()) result = addFrameOfReferenceModule(dataset); + if (result.good()) result = addEnhancedGeneralEquipmentModule(dataset); + if (result.good()) result = addManufacturing3DModelModule(dataset); + break; + + case DT_unknownDocument: + break; + } } - } - if (result.good()) - { - // according to C.24.2.1 on part 3, (0042,0012) is text/XML for CDA. - if (ftype == "cda") - result = dataset->putAndInsertString(DCM_MIMETypeOfEncapsulatedDocument, "text/XML"); - // according to A.45.1.4.1 on part 3, MIME Type is application/pdf for PDF. - if (ftype == "pdf") - result = dataset->putAndInsertString(DCM_MIMETypeOfEncapsulatedDocument, "application/pdf"); - // according to A.85.1.4.2 on part 3, MIME Type is model/stl. - if (ftype == "stl") - result = dataset->putAndInsertString(DCM_MIMETypeOfEncapsulatedDocument, "model/stl"); - } - // there is no way we could determine a meaningful series number, so we just use a constant. - if (result.good()) result = dataset->putAndInsertString(DCM_SeriesNumber, "1"); - // insert variable value attributes - if (result.good()) result = dataset->putAndInsertString(DCM_DocumentTitle, opt_documentTitle.c_str()); - if (result.good()) result = dataset->putAndInsertString(DCM_PatientName, opt_patientName.c_str()); - if (result.good()) result = dataset->putAndInsertString(DCM_PatientID, opt_patientID.c_str()); - if (result.good()) result = dataset->putAndInsertString(DCM_PatientBirthDate, opt_patientBirthdate.c_str()); - if (result.good()) result = dataset->putAndInsertString(DCM_PatientSex, opt_patientSex.c_str()); - if (result.good()) result = dataset->putAndInsertString(DCM_BurnedInAnnotation, opt_annotation ? "YES" : "NO"); - if (strlen(cda_mediaTypes.c_str()) > 0) - { - if (result.good()) result = dataset->putAndInsertString(DCM_ListOfMIMETypes, cda_mediaTypes.c_str()); - } - if (hl7_InstanceIdentifier.size() > 0) - { - if (result.good()) result = dataset->putAndInsertString(DCM_HL7InstanceIdentifier, hl7_InstanceIdentifier.c_str()); - } - OFStandard::snprintf(buf, sizeof(buf), "%ld", OFstatic_cast(long, opt_instance)); - if (result.good()) result = dataset->putAndInsertString(DCM_InstanceNumber, buf); - dcmGenerateUniqueIdentifier(buf, SITE_INSTANCE_UID_ROOT); - if (result.good()) result = dataset->putAndInsertString(DCM_StudyInstanceUID, opt_studyUID.c_str()); - if (result.good()) result = dataset->putAndInsertString(DCM_SeriesInstanceUID, opt_seriesUID.c_str()); - if (result.good()) result = dataset->putAndInsertString(DCM_SOPInstanceUID, buf); - // set instance creation date and time - OFString s; - if (result.good()) result = DcmDate::getCurrentDate(s); - if (result.good()) result = dataset->putAndInsertOFStringArray(DCM_InstanceCreationDate, s); - if (result.good()) result = DcmTime::getCurrentTime(s); - if (result.good()) result = dataset->putAndInsertOFStringArray(DCM_InstanceCreationTime, s); - return result; -} -OFCondition DcmEncapsulatedDocument::applyOverrideKeys(DcmDataset *outputDset) -{ - // replace specific keys by those in overrideKeys, copied from findscu - OFListConstIterator(OFString) path = opt_overrideKeys.begin(); - OFListConstIterator(OFString) endOfList = opt_overrideKeys.end(); - OFCondition cond; - DcmPathProcessor proc; - while (path != endOfList) - { - cond = proc.applyPathWithValue(outputDset, *path); - if (cond.bad()) + if (result.bad()) { - OFString err; - err += "Bad override key/path: "; - err += *path; - err += ": "; - err += cond.text(); - return makeOFCondition(OFM_dcmdata, 18, OF_error, err.c_str()); + DCMDATA_ERROR("Error while creating DICOM header: " << result.text()); } - path++; - } - return cond; + return result; } -void DcmEncapsulatedDocument::setOverrideKeys(const OFList &ovkeys) -{ - OFListConstIterator(OFString) it = ovkeys.begin(); - OFListConstIterator(OFString) end = ovkeys.end(); - while (it != end) - { - opt_overrideKeys.push_back(*it); - it++; - } -} -OFCondition DcmEncapsulatedDocument::saveFile(DcmFileFormat fileformat) +OFCondition DcmEncapsulatedDocument::applyOverrideKeys() { - return fileformat.saveFile(opt_ofname, opt_oxfer, opt_oenctype, opt_oglenc, - opt_opadenc, OFstatic_cast(Uint32, opt_filepad), - OFstatic_cast(Uint32, opt_itempad)); -} - -OFString DcmEncapsulatedDocument::getInputFileName() -{ - return opt_ifname; + DcmDataset *outputDset = dfile_.getDataset(); + // replace specific keys by those in overrideKeys, copied from findscu + OFListConstIterator(OFString) path = overrideKeys_.begin(); + OFListConstIterator(OFString) endOfList = overrideKeys_.end(); + OFCondition cond; + DcmPathProcessor proc; + if (path != endOfList) + { + DCMDATA_DEBUG("Applying override keys from command line"); + } + while (path != endOfList) + { + cond = proc.applyPathWithValue(outputDset, *path); + if (cond.bad()) + { + OFString err; + err += "Bad override key/path: "; + err += *path; + err += ": "; + err += cond.text(); + DCMDATA_ERROR(err.c_str()); + return makeOFCondition(OFM_dcmdata, 18, OF_error, err.c_str()); + } + path++; + } + return cond; } -void DcmEncapsulatedDocument::setInputFileName(OFString fName) -{ - opt_ifname = fName; -} -OFString DcmEncapsulatedDocument::getOutputFileName() +OFCondition DcmEncapsulatedDocument::saveFile() { - return opt_ofname; -} + OFCondition result = dfile_.saveFile( + ofname_, oxfer_, oenctype_, oglenc_, opadenc_, + OFstatic_cast(Uint32, filepad_), OFstatic_cast(Uint32, itempad_)); -void DcmEncapsulatedDocument::setOutputFileName(OFString fName) -{ - opt_ofname = fName; + if (result.bad()) + { + DCMDATA_ERROR(result.text() << ": writing file: '" << ofname_ << "'"); + } + return result; } -OFString DcmEncapsulatedDocument::getFileType() -{ - return ftype; -} -void DcmEncapsulatedDocument::setFileType(OFString fType) +OFString DcmEncapsulatedDocument::getInputFileName() { - ftype = fType; + return ifname_; } -E_TransferSyntax DcmEncapsulatedDocument::getTransferSyntax() -{ - return opt_oxfer; -} -DcmEncapsulatedDocument::~DcmEncapsulatedDocument() +OFString DcmEncapsulatedDocument::getOutputFileName() { + return ofname_; } diff --git a/dcmdata/libsrc/dcerror.cc b/dcmdata/libsrc/dcerror.cc index 3dfbd810..a27c93b5 100644 --- a/dcmdata/libsrc/dcerror.cc +++ b/dcmdata/libsrc/dcerror.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2022, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -79,11 +79,18 @@ makeOFConditionConst(EC_PixelDataExplLengthIllegal, OFM_dcmdata, 53, OF_err makeOFConditionConst(EC_ElemLengthExceeds32BitField, OFM_dcmdata, 54, OF_error, "Length of element value exceeds maximum of 32-bit length field" ); makeOFConditionConst(EC_CannotWriteJsonNumber, OFM_dcmdata, 55, OF_error, "Cannot write 'nan' or 'inf' as JSON number" ); makeOFConditionConst(EC_CannotWriteJsonInlineBinary, OFM_dcmdata, 56, OF_error, "JSON InlineBinary encoding not supported for compressed pixel data" ); -makeOFConditionConst(EC_XMLParseError, OFM_dcmdata, 57, OF_error, "XML parse error" ); -makeOFConditionConst(EC_XMLValidationFailure, OFM_dcmdata, 58, OF_error, "XML validation failure" ); -makeOFConditionConst(EC_SOPClassMismatch, OFM_dcmdata, 59, OF_error, "SOP class mismatch" ); +makeOFConditionConst(EC_XMLParseError, OFM_dcmdata, 57, OF_error, "XML parse error" ); +makeOFConditionConst(EC_XMLValidationFailure, OFM_dcmdata, 58, OF_error, "XML validation failure" ); +makeOFConditionConst(EC_SOPClassMismatch, OFM_dcmdata, 59, OF_error, "SOP Class mismatch" ); makeOFConditionConst(EC_UnknownUIDName, OFM_dcmdata, 60, OF_error, "Unknown UID name: No mapping to UID value defined" ); -makeOFConditionConst(EC_CannotWriteStringAsJsonNumber, OFM_dcmdata, 61, OF_error, "Cannot write IS/DS string as JSON number" ); +makeOFConditionConst(EC_CannotWriteStringAsJSONNumber, OFM_dcmdata, 61, OF_error, "Cannot write IS/DS string as JSON number" ); +makeOFConditionConst(EC_CannotWriteBulkDataFile, OFM_dcmdata, 62, OF_error, "Cannot write bulk data file" ); +makeOFConditionConst(EC_CannotWriteJSONMultiframe, OFM_dcmdata, 63, OF_error, "JSON encoding not supported for encapsulated multi-frame pixel data" ); +makeOFConditionConst(EC_InvalidJSONType, OFM_dcmdata, 64, OF_error, "Invalid JSON type" ); +makeOFConditionConst(EC_InvalidJSONContent, OFM_dcmdata, 65, OF_error, "Invalid JSON content" ); +makeOFConditionConst(EC_BulkDataURINotSupported, OFM_dcmdata, 66, OF_error, "BulkDataURI not yet supported" ); +makeOFConditionConst(EC_UnsupportedURIType, OFM_dcmdata, 67, OF_error, "Unsupported URI type" ); +makeOFConditionConst(EC_CommandLineFailed, OFM_dcmdata, 68, OF_error, "Execution of command line failed" ); const unsigned short EC_CODE_CannotSelectCharacterSet = 35; const unsigned short EC_CODE_CannotConvertCharacterSet = 36; diff --git a/dcmdata/libsrc/dcfilefo.cc b/dcmdata/libsrc/dcfilefo.cc index e8d6de68..b1a8c16c 100644 --- a/dcmdata/libsrc/dcfilefo.cc +++ b/dcmdata/libsrc/dcfilefo.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -52,7 +52,9 @@ DcmFileFormat::DcmFileFormat() : DcmSequenceOfItems(DCM_InternalUseTag), - FileReadMode(ERM_autoDetect) + FileReadMode(ERM_autoDetect), + ImplementationClassUID(OFFIS_IMPLEMENTATION_CLASS_UID), + ImplementationVersionName(OFFIS_DTK_IMPLEMENTATION_VERSION_NAME) { DcmMetaInfo *MetaInfo = new DcmMetaInfo(); DcmSequenceOfItems::itemList->insert(MetaInfo); @@ -67,7 +69,9 @@ DcmFileFormat::DcmFileFormat() DcmFileFormat::DcmFileFormat(DcmDataset *dataset, OFBool deepCopy) : DcmSequenceOfItems(DCM_InternalUseTag), - FileReadMode(ERM_autoDetect) + FileReadMode(ERM_autoDetect), + ImplementationClassUID(OFFIS_IMPLEMENTATION_CLASS_UID), + ImplementationVersionName(OFFIS_DTK_IMPLEMENTATION_VERSION_NAME) { DcmMetaInfo *MetaInfo = new DcmMetaInfo(); DcmSequenceOfItems::itemList->insert(MetaInfo); @@ -93,7 +97,9 @@ DcmFileFormat::DcmFileFormat(DcmDataset *dataset, DcmFileFormat::DcmFileFormat(const DcmFileFormat &old) : DcmSequenceOfItems(old), - FileReadMode(old.FileReadMode) + FileReadMode(old.FileReadMode), + ImplementationClassUID(old.ImplementationClassUID), + ImplementationVersionName(old.ImplementationVersionName) { } @@ -120,6 +126,8 @@ DcmFileFormat &DcmFileFormat::operator=(const DcmFileFormat &obj) { DcmSequenceOfItems::operator=(obj); FileReadMode = obj.FileReadMode; + ImplementationClassUID = obj.ImplementationClassUID; + ImplementationVersionName = obj.ImplementationVersionName; } return *this; @@ -265,12 +273,13 @@ OFCondition DcmFileFormat::writeJson(STD_NAMESPACE ostream &out, // ******************************** -OFCondition DcmFileFormat::checkMetaHeaderValue(DcmMetaInfo *metainfo, - DcmDataset *dataset, - const DcmTagKey &atagkey, - DcmObject *obj, - const E_TransferSyntax oxfer, - const E_FileWriteMode writeMode) +OFCondition DcmFileFormat::checkMetaHeaderValue( + DcmMetaInfo *metainfo, + DcmDataset *dataset, + const DcmTagKey &atagkey, + DcmObject *obj, + const E_TransferSyntax oxfer, + const E_FileWriteMode writeMode) const /* * This function checks if a particular data element of the file meta information header is * existent. If the element is not existent, it will be inserted. Additionally, this function @@ -360,7 +369,7 @@ OFCondition DcmFileFormat::checkMetaHeaderValue(DcmMetaInfo *metainfo, } if (elem->ident() == EVR_UI) { - if ((writeMode == EWM_updateMeta) || (elem->getLength() == 0)) + if ((writeMode == EWM_updateMeta) || (writeMode == EWM_createNewMeta) || (elem->getLength() == 0)) { if (dataset->search(DCM_SOPClassUID, stack).good() && (stack.top()->ident() == EVR_UI)) { @@ -402,7 +411,7 @@ OFCondition DcmFileFormat::checkMetaHeaderValue(DcmMetaInfo *metainfo, } if (elem->ident() == EVR_UI) { - if ((writeMode == EWM_updateMeta) || (elem->getLength() == 0)) + if ((writeMode == EWM_updateMeta) || (writeMode == EWM_createNewMeta) || (elem->getLength() == 0)) { if (dataset->search(DCM_SOPInstanceUID, stack).good() && (stack.top()->ident() == EVR_UI)) { @@ -468,8 +477,7 @@ OFCondition DcmFileFormat::checkMetaHeaderValue(DcmMetaInfo *metainfo, } if (elem->ident() == EVR_UI) { - const char *uid = OFFIS_IMPLEMENTATION_CLASS_UID; - OFstatic_cast(DcmUniqueIdentifier *, elem)->putString(uid); + OFstatic_cast(DcmUniqueIdentifier *, elem)->putString(ImplementationClassUID.c_str()); } } else if (tag == DCM_ImplementationVersionName) // (0002,0013) @@ -481,8 +489,7 @@ OFCondition DcmFileFormat::checkMetaHeaderValue(DcmMetaInfo *metainfo, } if (elem->ident() == EVR_SH) { - const char *uid = OFFIS_DTK_IMPLEMENTATION_VERSION_NAME; - OFstatic_cast(DcmShortString *, elem)->putString(uid); + OFstatic_cast(DcmShortString *, elem)->putString(ImplementationVersionName.c_str()); } } else if ((tag == DCM_SourceApplicationEntityTitle) || // (0002,0016) @@ -566,13 +573,27 @@ OFCondition DcmFileFormat::validateMetaInfo(const E_TransferSyntax oxfer, { DCMDATA_WARN("DcmFileFormat: Meta Information Header is not updated!"); } else { + /* in the following, we want to make sure all elements of the meta header */ + /* are existent in metinf and contain correct values */ + DcmStack stack; + /* start with empty file meta information */ if (writeMode == EWM_createNewMeta) + { + /* search, and if present, store and remove the media storage SOP class and instance UID. */ + metinf->search(DCM_MediaStorageSOPClassUID, stack, ESM_fromHere, OFFalse); + DcmElement *sopClassUID = metinf->remove(stack.top()); + metinf->search(DCM_MediaStorageSOPInstanceUID, stack, ESM_fromHere, OFFalse); + DcmElement *sopInstanceUID = metinf->remove(stack.top()); + + /* clear the meta-header and the search stack */ metinf->clear(); + stack.clear(); - /* in the following, we want to make sure all elements of the meta header */ - /* are existent in metinf and contain correct values */ - DcmStack stack; + /* re-insert SOP class UID and SOP instance UID */ + if (sopClassUID) metinf->insert(sopClassUID); + if (sopInstanceUID) metinf->insert(sopInstanceUID); + } /* DCM_FileMetaInformationGroupLength */ metinf->search(DCM_FileMetaInformationGroupLength, stack, ESM_fromHere, OFFalse); @@ -752,6 +773,11 @@ OFCondition DcmFileFormat::readUntilTag(DcmInputStream &inStream, // remember the parent dataset->setParent(this); } + + // initialize dataset transfer syntax members + // to make sure the values are correct even if the dataset is empty + dataset->initializeXfer(newxfer); + // check whether to read the dataset at all if (FileReadMode != ERM_metaOnly) { @@ -817,6 +843,12 @@ OFCondition DcmFileFormat::write(DcmOutputStream &outStream, * in the file meta information header. */ { + /* write as dataset (without meta header) */ + if (writeMode == EWM_dataset) + { + return getDataset()->write(outStream, oxfer, enctype, wcache, glenc, + padenc, padlen, subPadlen, instanceLength); + } /* if the transfer state of this is not initialized, this is an illegal call */ if (getTransferState() == ERW_notInitialized) errorFlag = EC_IllegalCall; @@ -975,6 +1007,7 @@ OFCondition DcmFileFormat::saveFile(const OFFilename &fileName, const Uint32 subPadLength, const E_FileWriteMode writeMode) { + /* save as dataset (without meta header) */ if (writeMode == EWM_dataset) { return getDataset()->saveFile(fileName, writeXfer, encodingType, groupLength, @@ -1076,8 +1109,10 @@ DcmMetaInfo *DcmFileFormat::getMetaInfo() { errorFlag = EC_Normal; DcmMetaInfo *meta = NULL; - if (itemList->seek_to(0) != NULL && itemList->get()->ident() == EVR_metainfo) - meta = OFstatic_cast(DcmMetaInfo *, itemList->get()); + // the meta information is the first item + DcmObject *object = itemList->seek(ELP_first); + if (object != NULL && object->ident() == EVR_metainfo) + meta = OFstatic_cast(DcmMetaInfo *, object); else errorFlag = EC_IllegalCall; return meta; @@ -1091,8 +1126,10 @@ DcmDataset *DcmFileFormat::getDataset() { errorFlag = EC_Normal; DcmDataset *data = NULL; - if (itemList->seek_to(1) != NULL && itemList->get()->ident() == EVR_dataset) - data = OFstatic_cast(DcmDataset *, itemList->get()); + // the dataset is the last item + DcmObject *object = itemList->seek(ELP_last); + if (object != NULL && object->ident() == EVR_dataset) + data = OFstatic_cast(DcmDataset *, object); else errorFlag = EC_IllegalCall; return data; @@ -1106,7 +1143,9 @@ DcmDataset *DcmFileFormat::getAndRemoveDataset() { errorFlag = EC_Normal; DcmDataset *data = NULL; - if (itemList->seek_to(1) != NULL && itemList->get()->ident() == EVR_dataset) + // the dataset is the last item + DcmObject *object = itemList->seek(ELP_last); + if (object != NULL && object->ident() == EVR_dataset) { data = OFstatic_cast(DcmDataset *, itemList->remove()); // forget about the parent @@ -1165,3 +1204,18 @@ OFCondition DcmFileFormat::convertToUTF8() // the DICOM defined term "ISO_IR 192" is used for "UTF-8" return convertCharacterSet("ISO_IR 192", 0 /*flags*/); } + +void DcmFileFormat::setImplementationClassUID(const OFString& implementationClassUID) +{ + ImplementationClassUID = implementationClassUID; +} + +void DcmFileFormat::setImplementationVersionName(const OFString& implementationVersionName) +{ + ImplementationVersionName = implementationVersionName; + if (ImplementationVersionName.length() > 16) + { + DCMDATA_WARN("DcmFileFormat: implementation version name too long"); + ImplementationVersionName.erase(16); + } +} diff --git a/dcmdata/libsrc/dcistrmf.cc b/dcmdata/libsrc/dcistrmf.cc index daed3ce7..9c0181a4 100644 --- a/dcmdata/libsrc/dcistrmf.cc +++ b/dcmdata/libsrc/dcistrmf.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2021, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -26,9 +26,7 @@ #include "dcmtk/dcmdata/dcerror.h" BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include -#endif #ifdef HAVE_IO_H #include #endif diff --git a/dcmdata/libsrc/dcistrms.cc b/dcmdata/libsrc/dcistrms.cc index 28741a2e..9dabf376 100644 --- a/dcmdata/libsrc/dcistrms.cc +++ b/dcmdata/libsrc/dcistrms.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2021, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -25,9 +25,7 @@ #include "dcmtk/dcmdata/dcerror.h" BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include -#endif #ifdef HAVE_IO_H #include #endif diff --git a/dcmdata/libsrc/dcistrmz.cc b/dcmdata/libsrc/dcistrmz.cc index 0a6a6fd9..c67aaab2 100644 --- a/dcmdata/libsrc/dcistrmz.cc +++ b/dcmdata/libsrc/dcistrmz.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2010, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -321,7 +321,7 @@ offile_off_t DcmZLibInputFilter::decompress(const void *buf, offile_off_t buflen * (where PDVs always have even length). * Everything else generates a warning. */ - DCMDATA_WARN("zlib: " << OFstatic_cast(ulong, count-1) << " pending input bytes in buffer."); + DCMDATA_WARN("DcmZLibInputFilter: " << OFstatic_cast(unsigned long, count-1) << " pending input bytes in buffer"); } } #endif @@ -366,7 +366,7 @@ offile_off_t DcmZLibInputFilter::decompress(const void *buf, offile_off_t buflen * (where PDVs always have even length). * Everything else generates a warning. */ - DCMDATA_WARN("zlib: " << OFstatic_cast(ulong, count-1) << " pending input bytes in buffer."); + DCMDATA_WARN("DcmZLibInputFilter: " << OFstatic_cast(unsigned long, count-1) << " pending input bytes in buffer"); } } #endif diff --git a/dcmdata/libsrc/dcitem.cc b/dcmdata/libsrc/dcitem.cc index 244dbb7b..62bf16f2 100644 --- a/dcmdata/libsrc/dcitem.cc +++ b/dcmdata/libsrc/dcitem.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -1050,10 +1050,11 @@ OFCondition DcmItem::readTagAndLength(DcmInputStream &inStream, swapIfNecessary(gLocalByteOrder, byteOrder, &elementTag, 2, 2); // tag has been read bytesRead = 4; + OFString readVR; DcmTag newTag(groupTag, elementTag); DcmEVR newEVR = newTag.getEVR(); // check whether tag is private - OFBool isPrivate = groupTag & 1; + const OFBool isPrivate = groupTag & 1; /* if the transfer syntax which was passed is an explicit VR syntax and if the current */ /* item is not a delimitation item (note that delimitation items do not have a VR), go */ @@ -1065,6 +1066,7 @@ OFCondition DcmItem::readTagAndLength(DcmInputStream &inStream, /* read 2 bytes */ inStream.read(vrstr, 2); + readVR = vrstr; /* create a corresponding DcmVR object */ DcmVR vr(vrstr); @@ -1193,9 +1195,28 @@ OFCondition DcmItem::readTagAndLength(DcmInputStream &inStream, if ((vrSize > 1) && (valueLength % vrSize != 0)) { /* warning is only reported for standard, fixed-size VRs that require more than 1 byte per value */ - DCMDATA_WARN("DcmItem: Length of element " << newTag << " is not a multiple of " << vrSize << " (VR=" << vr.getVRName() << ")"); + if (valueLength == DCM_UndefinedLength) + { + /* check whether the VR supports undefined length for the length field */ + if (!vr.supportsUndefinedLength()) + { + DCMDATA_WARN("DcmItem: Dubious use of undefined length for element " << newTag + << " with VR=" << vr.getVRName()); + } + } else { + DCMDATA_WARN("DcmItem: Length of element " << newTag << " is not a multiple of " << vrSize + << " (VR=" << vr.getVRName() << ")"); + } } } + /* check whether the correct VR is used for PixelData element in encapsulated format */ + if ((newTag == DCM_PixelData) && xferSyn.usesEncapsulatedFormat() && + (valueLength == DCM_UndefinedLength) && (readVR != "OB") /* this is the VR that was read */) + { + DCMDATA_WARN("DcmItem: Wrong VR for encapsulated PixelData " << newTag + << ", should be 'OB' instead of '" << readVR << "'"); + } + /* if the value in the length field is odd, print an error message */ if ((valueLength & 1) && (valueLength != DCM_UndefinedLength)) { @@ -2413,7 +2434,7 @@ OFCondition DcmItem::findAndGetElements(const DcmTagKey &tagKey, } -OFCondition DcmItem::findAndGetString(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetString(const DcmTagKey &tagKey, const char *&value, const OFBool searchIntoSub) { @@ -2432,7 +2453,7 @@ OFCondition DcmItem::findAndGetString(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetString(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetString(const DcmTagKey &tagKey, const char *&value, Uint32 &length, const OFBool searchIntoSub) @@ -2455,7 +2476,7 @@ OFCondition DcmItem::findAndGetString(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetOFString(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetOFString(const DcmTagKey &tagKey, OFString &value, const unsigned long pos, const OFBool searchIntoSub) @@ -2475,7 +2496,7 @@ OFCondition DcmItem::findAndGetOFString(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetOFStringArray(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetOFStringArray(const DcmTagKey &tagKey, OFString &value, const OFBool searchIntoSub) { @@ -2494,7 +2515,7 @@ OFCondition DcmItem::findAndGetOFStringArray(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetUint8(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetUint8(const DcmTagKey &tagKey, Uint8 &value, const unsigned long pos, const OFBool searchIntoSub) @@ -2514,7 +2535,7 @@ OFCondition DcmItem::findAndGetUint8(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetUint8Array(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetUint8Array(const DcmTagKey &tagKey, const Uint8 *&value, unsigned long *count, const OFBool searchIntoSub) @@ -2544,7 +2565,7 @@ OFCondition DcmItem::findAndGetUint8Array(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetUint16(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetUint16(const DcmTagKey &tagKey, Uint16 &value, const unsigned long pos, const OFBool searchIntoSub) @@ -2564,7 +2585,7 @@ OFCondition DcmItem::findAndGetUint16(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetUint16Array(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetUint16Array(const DcmTagKey &tagKey, const Uint16 *&value, unsigned long *count, const OFBool searchIntoSub) @@ -2597,7 +2618,7 @@ OFCondition DcmItem::findAndGetUint16Array(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetSint16(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetSint16(const DcmTagKey &tagKey, Sint16 &value, const unsigned long pos, const OFBool searchIntoSub) @@ -2617,7 +2638,7 @@ OFCondition DcmItem::findAndGetSint16(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetSint16Array(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetSint16Array(const DcmTagKey &tagKey, const Sint16 *&value, unsigned long *count, const OFBool searchIntoSub) @@ -2647,7 +2668,7 @@ OFCondition DcmItem::findAndGetSint16Array(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetUint32(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetUint32(const DcmTagKey &tagKey, Uint32 &value, const unsigned long pos, const OFBool searchIntoSub) @@ -2667,7 +2688,7 @@ OFCondition DcmItem::findAndGetUint32(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetUint32Array(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetUint32Array(const DcmTagKey &tagKey, const Uint32 *&value, unsigned long *count, const OFBool searchIntoSub) @@ -2697,7 +2718,7 @@ OFCondition DcmItem::findAndGetUint32Array(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetSint32(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetSint32(const DcmTagKey &tagKey, Sint32 &value, const unsigned long pos, const OFBool searchIntoSub) @@ -2717,7 +2738,7 @@ OFCondition DcmItem::findAndGetSint32(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetSint32Array(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetSint32Array(const DcmTagKey &tagKey, const Sint32 *&value, unsigned long *count, const OFBool searchIntoSub) @@ -2747,7 +2768,7 @@ OFCondition DcmItem::findAndGetSint32Array(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetUint64(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetUint64(const DcmTagKey &tagKey, Uint64 &value, const unsigned long pos, const OFBool searchIntoSub) @@ -2767,7 +2788,7 @@ OFCondition DcmItem::findAndGetUint64(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetUint64Array(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetUint64Array(const DcmTagKey &tagKey, const Uint64 *&value, unsigned long *count, const OFBool searchIntoSub) @@ -2797,7 +2818,7 @@ OFCondition DcmItem::findAndGetUint64Array(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetSint64(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetSint64(const DcmTagKey &tagKey, Sint64 &value, const unsigned long pos, const OFBool searchIntoSub) @@ -2817,7 +2838,7 @@ OFCondition DcmItem::findAndGetSint64(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetSint64Array(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetSint64Array(const DcmTagKey &tagKey, const Sint64 *&value, unsigned long *count, const OFBool searchIntoSub) @@ -2847,7 +2868,7 @@ OFCondition DcmItem::findAndGetSint64Array(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetLongInt(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetLongInt(const DcmTagKey &tagKey, long int &value, const unsigned long pos, const OFBool searchIntoSub) @@ -2897,7 +2918,7 @@ OFCondition DcmItem::findAndGetLongInt(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetFloat32(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetFloat32(const DcmTagKey &tagKey, Float32 &value, const unsigned long pos, const OFBool searchIntoSub) @@ -2917,7 +2938,7 @@ OFCondition DcmItem::findAndGetFloat32(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetFloat32Array(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetFloat32Array(const DcmTagKey &tagKey, const Float32 *&value, unsigned long *count, const OFBool searchIntoSub) @@ -2947,7 +2968,7 @@ OFCondition DcmItem::findAndGetFloat32Array(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetFloat64(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetFloat64(const DcmTagKey &tagKey, Float64 &value, const unsigned long pos, const OFBool searchIntoSub) @@ -2967,7 +2988,7 @@ OFCondition DcmItem::findAndGetFloat64(const DcmTagKey& tagKey, } -OFCondition DcmItem::findAndGetFloat64Array(const DcmTagKey& tagKey, +OFCondition DcmItem::findAndGetFloat64Array(const DcmTagKey &tagKey, const Float64 *&value, unsigned long *count, const OFBool searchIntoSub) @@ -3095,7 +3116,7 @@ OFCondition DcmItem::findAndGetSequenceItem(const DcmTagKey &seqTagKey, /* --- findOrCreate functions: find an element or create a new one --- */ -OFCondition DcmItem::findOrCreateSequenceItem(const DcmTag& seqTag, +OFCondition DcmItem::findOrCreateSequenceItem(const DcmTag &seqTag, DcmItem *&item, const signed long itemNum) { @@ -3270,7 +3291,7 @@ OFCondition DcmItem::findAndDeleteSequenceItem(const DcmTagKey &seqTagKey, /* --- putAndInsert functions: put value and insert new element --- */ -OFCondition DcmItem::putAndInsertString(const DcmTag& tag, +OFCondition DcmItem::putAndInsertString(const DcmTag &tag, const char *value, const OFBool replaceOld) { @@ -3281,7 +3302,7 @@ OFCondition DcmItem::putAndInsertString(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertString(const DcmTag& tag, +OFCondition DcmItem::putAndInsertString(const DcmTag &tag, const char *value, const Uint32 length, const OFBool replaceOld) @@ -3409,7 +3430,7 @@ OFCondition DcmItem::putAndInsertString(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertOFStringArray(const DcmTag& tag, +OFCondition DcmItem::putAndInsertOFStringArray(const DcmTag &tag, const OFString &value, const OFBool replaceOld) { @@ -3493,7 +3514,7 @@ OFCondition DcmItem::putAndInsertOFStringArray(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertUint8Array(const DcmTag& tag, +OFCondition DcmItem::putAndInsertUint8Array(const DcmTag &tag, const Uint8 *value, const unsigned long count, const OFBool replaceOld) @@ -3545,7 +3566,7 @@ OFCondition DcmItem::putAndInsertUint8Array(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertUint16(const DcmTag& tag, +OFCondition DcmItem::putAndInsertUint16(const DcmTag &tag, const Uint16 value, const unsigned long pos, const OFBool replaceOld) @@ -3587,7 +3608,7 @@ OFCondition DcmItem::putAndInsertUint16(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertUint16Array(const DcmTag& tag, +OFCondition DcmItem::putAndInsertUint16Array(const DcmTag &tag, const Uint16 *value, const unsigned long count, const OFBool replaceOld) @@ -3600,13 +3621,13 @@ OFCondition DcmItem::putAndInsertUint16Array(const DcmTag& tag, case EVR_AT: elem = new DcmAttributeTag(tag); break; + case EVR_US: + elem = new DcmUnsignedShort(tag); + break; case EVR_lt: case EVR_OW: elem = new DcmOtherByteOtherWord(tag); break; - case EVR_US: - elem = new DcmUnsignedShort(tag); - break; case EVR_ox: /* special handling */ if (tag == DCM_PixelData) @@ -3650,7 +3671,7 @@ OFCondition DcmItem::putAndInsertUint16Array(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertSint16(const DcmTag& tag, +OFCondition DcmItem::putAndInsertSint16(const DcmTag &tag, const Sint16 value, const unsigned long pos, const OFBool replaceOld) @@ -3692,7 +3713,7 @@ OFCondition DcmItem::putAndInsertSint16(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertSint16Array(const DcmTag& tag, +OFCondition DcmItem::putAndInsertSint16Array(const DcmTag &tag, const Sint16 *value, const unsigned long count, const OFBool replaceOld) @@ -3734,7 +3755,7 @@ OFCondition DcmItem::putAndInsertSint16Array(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertUint32(const DcmTag& tag, +OFCondition DcmItem::putAndInsertUint32(const DcmTag &tag, const Uint32 value, const unsigned long pos, const OFBool replaceOld) @@ -3774,7 +3795,7 @@ OFCondition DcmItem::putAndInsertUint32(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertUint32Array(const DcmTag& tag, +OFCondition DcmItem::putAndInsertUint32Array(const DcmTag &tag, const Uint32 *value, const unsigned long count, const OFBool replaceOld) @@ -3784,12 +3805,12 @@ OFCondition DcmItem::putAndInsertUint32Array(const DcmTag& tag, DcmElement *elem = NULL; switch(tag.getEVR()) { - case EVR_OL: - elem = new DcmOtherLong(tag); - break; case EVR_UL: elem = new DcmUnsignedLong(tag); break; + case EVR_OL: + elem = new DcmOtherLong(tag); + break; case EVR_UNKNOWN: /* Unknown VR, e.g. tag not found in data dictionary */ status = EC_UnknownVR; @@ -3814,7 +3835,7 @@ OFCondition DcmItem::putAndInsertUint32Array(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertSint32(const DcmTag& tag, +OFCondition DcmItem::putAndInsertSint32(const DcmTag &tag, const Sint32 value, const unsigned long pos, const OFBool replaceOld) @@ -3851,7 +3872,7 @@ OFCondition DcmItem::putAndInsertSint32(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertSint32Array(const DcmTag& tag, +OFCondition DcmItem::putAndInsertSint32Array(const DcmTag &tag, const Sint32 *value, const unsigned long count, const OFBool replaceOld) @@ -3888,7 +3909,161 @@ OFCondition DcmItem::putAndInsertSint32Array(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertFloat32(const DcmTag& tag, +OFCondition DcmItem::putAndInsertUint64(const DcmTag &tag, + const Uint64 value, + const unsigned long pos, + const OFBool replaceOld) +{ + OFCondition status = EC_Normal; + /* create new element */ + DcmElement *elem = NULL; + switch(tag.getEVR()) + { + case EVR_UV: + elem = new DcmUnsigned64bitVeryLong(tag); + break; + case EVR_OV: + elem = new DcmOther64bitVeryLong(tag); + break; + case EVR_UNKNOWN: + /* Unknown VR, e.g. tag not found in data dictionary */ + status = EC_UnknownVR; + break; + default: + status = EC_IllegalCall; + break; + } + if (elem != NULL) + { + /* put value */ + status = elem->putUint64(value, pos); + /* insert into dataset/item */ + if (status.good()) + status = insert(elem, replaceOld); + /* could not be inserted, therefore, delete it immediately */ + if (status.bad()) + delete elem; + } else if (status.good()) + status = EC_MemoryExhausted; + return status; +} + + +OFCondition DcmItem::putAndInsertUint64Array(const DcmTag &tag, + const Uint64 *value, + const unsigned long count, + const OFBool replaceOld) +{ + OFCondition status = EC_Normal; + /* create new element */ + DcmElement *elem = NULL; + switch(tag.getEVR()) + { + case EVR_UV: + elem = new DcmUnsigned64bitVeryLong(tag); + break; + case EVR_OV: + elem = new DcmOther64bitVeryLong(tag); + break; + case EVR_UNKNOWN: + /* Unknown VR, e.g. tag not found in data dictionary */ + status = EC_UnknownVR; + break; + default: + status = EC_IllegalCall; + break; + } + if (elem != NULL) + { + /* put value */ + status = elem->putUint64Array(value, count); + /* insert into dataset/item */ + if (status.good()) + status = insert(elem, replaceOld); + /* could not be inserted, therefore, delete it immediately */ + if (status.bad()) + delete elem; + } else if (status.good()) + status = EC_MemoryExhausted; + return status; +} + + +OFCondition DcmItem::putAndInsertSint64(const DcmTag &tag, + const Sint64 value, + const unsigned long pos, + const OFBool replaceOld) +{ + OFCondition status = EC_Normal; + /* create new element */ + DcmElement *elem = NULL; + switch(tag.getEVR()) + { + case EVR_SV: + elem = new DcmSigned64bitVeryLong(tag); + break; + case EVR_UNKNOWN: + /* Unknown VR, e.g. tag not found in data dictionary */ + status = EC_UnknownVR; + break; + default: + status = EC_IllegalCall; + break; + } + if (elem != NULL) + { + /* put value */ + status = elem->putSint64(value, pos); + /* insert into dataset/item */ + if (status.good()) + status = insert(elem, replaceOld); + /* could not be inserted, therefore, delete it immediately */ + if (status.bad()) + delete elem; + } else if (status.good()) + status = EC_MemoryExhausted; + return status; +} + + +OFCondition DcmItem::putAndInsertSint64Array(const DcmTag &tag, + const Sint64 *value, + const unsigned long count, + const OFBool replaceOld) +{ + OFCondition status = EC_Normal; + /* create new element */ + DcmElement *elem = NULL; + switch(tag.getEVR()) + { + case EVR_SV: + elem = new DcmSigned64bitVeryLong(tag); + break; + case EVR_UNKNOWN: + /* Unknown VR, e.g. tag not found in data dictionary */ + status = EC_UnknownVR; + break; + default: + status = EC_IllegalCall; + break; + } + if (elem != NULL) + { + /* put value */ + status = elem->putSint64Array(value, count); + /* insert into dataset/item */ + if (status.good()) + status = insert(elem, replaceOld); + /* could not be inserted, therefore, delete it immediately */ + if (status.bad()) + delete elem; + } else if (status.good()) + status = EC_MemoryExhausted; + return status; +} + + +OFCondition DcmItem::putAndInsertFloat32(const DcmTag &tag, const Float32 value, const unsigned long pos, const OFBool replaceOld) @@ -3928,7 +4103,7 @@ OFCondition DcmItem::putAndInsertFloat32(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertFloat32Array(const DcmTag& tag, +OFCondition DcmItem::putAndInsertFloat32Array(const DcmTag &tag, const Float32 *value, const unsigned long count, const OFBool replaceOld) @@ -3968,7 +4143,7 @@ OFCondition DcmItem::putAndInsertFloat32Array(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertFloat64(const DcmTag& tag, +OFCondition DcmItem::putAndInsertFloat64(const DcmTag &tag, const Float64 value, const unsigned long pos, const OFBool replaceOld) @@ -4011,7 +4186,7 @@ OFCondition DcmItem::putAndInsertFloat64(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertFloat64Array(const DcmTag& tag, +OFCondition DcmItem::putAndInsertFloat64Array(const DcmTag &tag, const Float64 *value, const unsigned long count, const OFBool replaceOld) @@ -4051,7 +4226,7 @@ OFCondition DcmItem::putAndInsertFloat64Array(const DcmTag& tag, } -OFCondition DcmItem::putAndInsertTagKey(const DcmTag& tag, +OFCondition DcmItem::putAndInsertTagKey(const DcmTag &tag, const DcmTagKey &value, const unsigned long pos, const OFBool replaceOld) @@ -4091,7 +4266,7 @@ OFCondition DcmItem::putAndInsertTagKey(const DcmTag& tag, // ******************************** -OFCondition DcmItem::insertEmptyElement(const DcmTag& tag, +OFCondition DcmItem::insertEmptyElement(const DcmTag &tag, const OFBool replaceOld) { OFCondition status = EC_Normal; @@ -4375,11 +4550,11 @@ void DcmItem::updateSpecificCharacterSet(OFCondition &status, // delete Specific Character Set (0008,0005) data element (type 1C) if (findAndDeleteElement(DCM_SpecificCharacterSet, OFFalse /*allOccurrences*/, OFFalse /*searchIntoSub*/).good()) { - DCMDATA_DEBUG("DcmItem::convertCharacterSet() deleted element SpecificCharacterSet " + DCMDATA_DEBUG("DcmItem::updateSpecificCharacterSet() deleted element SpecificCharacterSet " << DCM_SpecificCharacterSet << " during the conversion to " << encoding << " encoding"); } } else { - DCMDATA_DEBUG("DcmItem::convertCharacterSet() updating value of element SpecificCharacterSet " + DCMDATA_DEBUG("DcmItem::updateSpecificCharacterSet() updating value of element SpecificCharacterSet " << DCM_SpecificCharacterSet << " to '" << toCharset << "'"); // update/set value of Specific Character Set (0008,0005) if needed status = putAndInsertOFStringArray(DCM_SpecificCharacterSet, toCharset); @@ -4757,13 +4932,13 @@ OFCondition DcmItem::newDicomElement(DcmElement *&newElement, if (dcmIgnoreParsingErrors.get()) { // ignore parse error, keep VR unchanged - DCMDATA_WARN("DcmItem: VOI LUT Sequence with VR=OW and explicit length encountered."); + DCMDATA_WARN("DcmItem: VOI LUT Sequence with VR=OW and explicit length encountered"); newElement = new DcmOtherByteOtherWord(tag, length); } else { // bail out with an error - DCMDATA_ERROR("DcmItem: VOI LUT Sequence with VR=OW and explicit length encountered."); + DCMDATA_ERROR("DcmItem: VOI LUT Sequence with VR=OW and explicit length encountered"); l_error = EC_VOI_LUT_OBOW; } } @@ -4775,8 +4950,11 @@ OFCondition DcmItem::newDicomElement(DcmElement *&newElement, // special handling for private pixel data (compressed or uncompressed) if (newTag.getEVR() == EVR_px) { - DCMDATA_WARN("Found private element " << tag << " with VR " << tag.getVRName() - << " and undefined length, reading a pixel sequence according to data dictionary"); + if (length == DCM_UndefinedLength) + { + DCMDATA_WARN("DcmItem: Found private element " << tag << " with VR=" << tag.getVRName() + << " and undefined length, reading a pixel sequence according to data dictionary"); + } newElement = new DcmPixelData(tag, length); } } @@ -4826,10 +5004,10 @@ OFCondition DcmItem::newDicomElement(DcmElement *&newElement, newTag.setVR(DcmVR(EVR_SQ)); // on writing we will handle this element as SQ, not UN if (dcmEnableCP246Support.get()) { - DCMDATA_WARN("Found element " << newTag << " with VR UN and undefined length, " + DCMDATA_WARN("DcmItem: Found element " << newTag << " with VR=UN and undefined length, " << "reading a sequence with transfer syntax LittleEndianImplicit (CP-246)"); } else { - DCMDATA_WARN("Found element " << newTag << " with VR UN and undefined length"); + DCMDATA_WARN("DcmItem: Found element " << newTag << " with VR=UN and undefined length"); } newElement = new DcmSequenceOfItems(newTag, length, dcmEnableCP246Support.get()); } else { diff --git a/dcmdata/libsrc/dcjson.cc b/dcmdata/libsrc/dcjson.cc index dbbbe930..f23ab525 100644 --- a/dcmdata/libsrc/dcjson.cc +++ b/dcmdata/libsrc/dcjson.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2016-2023, OFFIS e.V. + * Copyright (C) 2016-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -24,6 +24,11 @@ #include "dcmtk/dcmdata/dcjson.h" #include "dcmtk/ofstd/ofdefine.h" #include "dcmtk/ofstd/ofstring.h" +#include "dcmtk/ofstd/offile.h" +#include "dcmtk/ofstd/ofsha256.h" +#include "dcmtk/ofstd/ofstream.h" +#include "dcmtk/dcmdata/dctypes.h" +#include "dcmtk/dcmdata/dcerror.h" #include @@ -57,7 +62,7 @@ void DcmJsonFormat::escapeControlCharacters(STD_NAMESPACE ostream &out, const OF out << "\\f"; break; default: - //escapes all other control characters + // escapes all other control characters if (c >= '\0' && c < ' ') { out << "\\u" << STD_NAMESPACE hex @@ -79,7 +84,7 @@ void DcmJsonFormat::normalizeDecimalString(OFString &value) // These are permitted in DICOM but not in JSON. size_t pos; while (OFString_npos != (pos = value.find('+'))) - value.erase(pos,1); + value.erase(pos,1); // check if the first character is a minus sign. // if so, remove it and set "minus" to true @@ -114,28 +119,27 @@ void DcmJsonFormat::normalizeDecimalString(OFString &value) // with a period if (OFString_npos != (pos = value.find('.'))) { - if (pos == value.length() -1) - { - // number ends with a period. Add a zero - value.append("0"); - } - else if ((value[pos+1] < '0') || (value[pos+1] > '9')) - { - // no digit after period. Insert a zero - value.insert(pos+1, "0"); - } + if (pos == value.length() -1) + { + // number ends with a period. Add a zero + value.append("0"); + } + else if ((value[pos+1] < '0') || (value[pos+1] > '9')) + { + // no digit after period. Insert a zero + value.insert(pos+1, "0"); + } } - } // Formats the number to JSON standard as IntegerString void DcmJsonFormat::normalizeIntegerString(OFString &value) { // remove all plus characters that may occur in the string. - // These are permitted in DICOM but not in Json. + // These are permitted in DICOM but not in JSON. size_t pos; while (OFString_npos != (pos = value.find('+'))) - value.erase(pos,1); + value.erase(pos,1); OFBool minus = OFFalse; @@ -243,19 +247,173 @@ void DcmJsonFormat::printNextArrayElementPrefix(STD_NAMESPACE ostream &out) out << "," << newline() << indent(); } -// Method for holding and determine if BulkDataURI should be printed. -// This also manipulate uri String, if BulkDataURI should be printed. -OFBool DcmJsonFormat::asBulkDataURI(const DcmTagKey& /*tag*/, OFString& /*uri*/) +OFBool DcmJsonFormat::asBulkDataURI(const DcmTagKey& /*tag*/, Uint32 len) const +{ + // return OFFalse if bulk data is disabled + if (minBulkDataSize < 0) return OFFalse; + + // return OFFalse if the attribute value is too small + size_t minSize = (OFstatic_cast(size_t, minBulkDataSize)); + if ((minSize << 10) > len) return OFFalse; + + return OFTrue; +} + +void DcmJsonFormat::getBulkDataDirectory(OFString& directory) const +{ + directory = bulkDataDirectory; +} + +void DcmJsonFormat::getBulkDataURIPrefix(OFString& prefix) const +{ + prefix = bulkDataURIPrefix; +} + +void DcmJsonFormat::setMinBulkSize(ssize_t min_bulk_size) { - return OFFalse; + minBulkDataSize = min_bulk_size; } -//Class for formatted output +void DcmJsonFormat::setBulkURIPrefix(const char *bulk_uri_prefix) +{ + if (bulk_uri_prefix) + { + bulkDataURIPrefix = bulk_uri_prefix; + + // if the URI prefix does not end with "/", silently add this character + if ((bulkDataURIPrefix.length() > 0) && (bulkDataURIPrefix[bulkDataURIPrefix.length()-1] != '/')) + { + bulkDataURIPrefix.append("/"); + } + } + else bulkDataURIPrefix = ""; +} + +void DcmJsonFormat::setBulkDir(const char *bulk_dir) +{ + if (bulk_dir) + { + bulkDataDirectory = bulk_dir; + } + else bulkDataDirectory = "."; + + // if the directory name does not end with a path separator, silently add one + if ((bulkDataDirectory.length() > 0) && (bulkDataDirectory[bulkDataDirectory.length()-1] != '/') && (bulkDataDirectory[bulkDataDirectory.length()-1] != PATH_SEPARATOR)) + { + bulkDataDirectory.append(1, PATH_SEPARATOR); + } +} + + +OFCondition DcmJsonFormat::writeBulkData( + STD_NAMESPACE ostream &out, + const DcmTagKey& /*tagkey*/, + Uint32 len, + Uint8 *byteValues, + const char *extension) +{ + /* for an empty value field, we do not need to do anything */ + if (len > 0) + { + OFString bulkDataURI; + getBulkDataURIPrefix(bulkDataURI); + + /* compute SHA-256 checksum */ + size_t vallen = OFstatic_cast(size_t, len); + OFSHA256 sha256; + Uint8 hash[32]; + sha256.update(byteValues, vallen); + sha256.final(hash); + + /* determine filename and path */ + OFString bulkname; + char hashstring[3]; + for (int i=0; i < 32; ++i) + { + OFStandard::snprintf(hashstring, sizeof(hashstring), "%02x", hash[i]); + bulkname.append(hashstring); + } + if (extension) bulkname.append(extension); + + OFString bulkpath; + getBulkDataDirectory(bulkpath); + + /* bulkpath already ends with a path separator, just add the file name */ + bulkpath.append(bulkname); + + /* check if file already exists. In this case, the file content is the same + * we would create now since the SHA-256 checksum is the same. So we can just + * use the existing file. + */ + if (! OFStandard::fileExists(bulkpath)) + { + OFFile bulkfile; + if (! bulkfile.fopen(bulkpath.c_str(), "wb")) + { + DCMDATA_ERROR("Unable to create bulk data file '" << bulkpath << "'"); + return EC_CannotWriteBulkDataFile; + } + if (vallen != bulkfile.fwrite(byteValues, 1, vallen)) + { + DCMDATA_ERROR("Unable to write bulk data to file '" << bulkpath << "'"); + return EC_CannotWriteBulkDataFile; + } + if (bulkfile.fclose()) + { + DCMDATA_ERROR("Unable to close bulk data file '" << bulkpath << "'"); + return EC_CannotWriteBulkDataFile; + } + } + + /* return defined BulkDataURI associated with `tagKey` */ + printBulkDataURIPrefix(out); + bulkDataURI.append(bulkname); + DcmJsonFormat::printString(out, bulkDataURI); + } + + return EC_Normal; +} + + +OFCondition DcmJsonFormat::writeBinaryAttribute( + STD_NAMESPACE ostream &out, + const DcmTagKey& tagkey, + Uint32 len, + Uint8 *byteValues, + const char *extension) +{ + OFCondition result = EC_Normal; + + /* for an empty value field, we do not need to do anything */ + if (len > 0) + { + if (asBulkDataURI(tagkey, len)) + { + result = writeBulkData(out, tagkey, len, byteValues, extension); + } + else + { + /* encode binary data as Base64 */ + printInlineBinaryPrefix(out); + out << "\""; + /* adjust byte order to little endian */ + OFStandard::encodeBase64(out, byteValues, OFstatic_cast(size_t, len)); + out << "\""; + } + } + + return result; +} + + +// -------------------------------------------------------------------------- +// Class for formatted output +// -------------------------------------------------------------------------- + DcmJsonFormatPretty::DcmJsonFormatPretty(const OFBool printMetaInfo) : DcmJsonFormat(printMetaInfo) , m_IndentionLevel(0) { - } void DcmJsonFormatPretty::printIndention(STD_NAMESPACE ostream& out) @@ -285,22 +443,26 @@ OFString DcmJsonFormatPretty::space() return " "; } +// -------------------------------------------------------------------------- +// Class for unformatted output +// -------------------------------------------------------------------------- -//Class for unformatted output DcmJsonFormatCompact::DcmJsonFormatCompact(const OFBool printMetaInfo) : DcmJsonFormat(printMetaInfo) { - } void DcmJsonFormatCompact::printIndention(STD_NAMESPACE ostream& /*out*/) -{} +{ +} void DcmJsonFormatCompact::increaseIndention() -{} +{ +} void DcmJsonFormatCompact::decreaseIndention() -{} +{ +} OFString DcmJsonFormatCompact::newline() { diff --git a/dcmdata/libsrc/dcjsonrd.cc b/dcmdata/libsrc/dcjsonrd.cc new file mode 100644 index 00000000..064dc350 --- /dev/null +++ b/dcmdata/libsrc/dcjsonrd.cc @@ -0,0 +1,1616 @@ +/* + * + * Copyright (C) 2024-2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmdata + * + * Author: Tingyan Xu, Marco Eichelberg + * + * Purpose: Class for converting JSON DICOM documents to binary DICOM files + * + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/dcmdata/dcjsonrd.h" +#include "dcmtk/ofstd/ofcond.h" /* for class OFCondition */ +#include "dcmtk/ofstd/offile.h" /* for class OFFile */ +#include "dcmtk/dcmdata/dcerror.h" /* for dcmdata logger macros */ +#include "dcmtk/dcmdata/dcfilefo.h" /* for class DcmFileFormat */ +#include "dcmtk/dcmdata/dcmetinf.h" +#include "dcmtk/dcmdata/dcdeftag.h" /* for tag keys */ +#include "dcmtk/dcmdata/dcswap.h" +#include "dcmtk/dcmdata/dcvrov.h" /* for DcmOther64bitVeryLong */ + +#include /* for INT_MAX */ + +#ifdef HAVE_WINDOWS_H +#include +#endif + +// Private creator identification string for files +// containing a list of datasets as a private sequence +#define JSON2DCM_PRIVATE_RESERVATION "JSON2DCM_LIST_OF_DATASETS" + +// Begin with a 1 MByte buffer when reading from stdin. +// Buffer size will increase if necessary. +#define JSON2DCM_STDIN_BLOCKSIZE 1048576 + + +DcmJSONReader::DcmJSONReader() +: jsonDataset_(NULL) +, jsonDatasetLen_(0) +, tokenArray_(NULL) +, tokenNumber_(0) +, ignoreBulkdataURIPolicy_(OFFalse) +, stopOnErrorPolicy_(OFTrue) +, ignoreMetaInfoPolicy_(OFFalse) +, arrayHandlingPolicy_(-1) +, xferSyntax_(EXS_LittleEndianExplicit) +, permittedBulkdataDirs_() +{ +} + + +DcmJSONReader::~DcmJSONReader() +{ + clear(); +} + + +void DcmJSONReader::clear() +{ + delete[] jsonDataset_; + jsonDataset_ = NULL; + delete[] tokenArray_; + tokenArray_ = NULL; + jsonDatasetLen_ = 0; + tokenNumber_ = 0; +} + + +OFCondition DcmJSONReader::readJSONFile(const char *ifname) +{ + if (NULL == ifname) return EC_IllegalParameter; + + OFFile jsonFile; + if (! jsonFile.fopen(ifname, "rb")) + { + OFString s("(unknown error code)"); + jsonFile.getLastErrorString(s); + DCMDATA_ERROR("failed to open JSON file '" << ifname << "': " << s); + return makeOFCondition(OFM_dcmdata, 18, OF_error, s.c_str()); + } + + // obtain file size + const size_t len = OFStandard::getFileSize(ifname); + char *jsonString = new (std::nothrow) char[len + 1]; + if (jsonString == NULL) + { + DCMDATA_ERROR("out of memory: failed to allocate buffer for JSON file"); + return EC_MemoryExhausted; + } + + // read the whole file to buffer + jsonString[len] = '\0'; + size_t res = jsonFile.fread(jsonString, 1, len); + jsonFile.fclose(); + if (res != len) + { + OFString s("(unknown error code)"); + jsonFile.getLastErrorString(s); + delete[] jsonString; + return makeOFCondition(OFM_dcmdata, 18, OF_error, s.c_str()); + } + + // file was successfully read into buffer. Store the result, and in the process, + // clear the token array which is now invalid + clear(); + jsonDataset_ = jsonString; + jsonDatasetLen_ = len; + + return EC_Normal; +} + + +OFCondition DcmJSONReader::readJSONFromStdin() +{ + size_t bufSize = JSON2DCM_STDIN_BLOCKSIZE; + size_t bufFree; + size_t bytesInBuffer = 0; + size_t readResult; + char *jsonString = NULL;; + char *oldBuf = NULL; + char *writePtr; + do + { + if (jsonString == NULL) jsonString = new (std::nothrow) char[bufSize+1]; + if (jsonString == NULL) + { + DCMDATA_ERROR("out of memory: failed to allocate buffer for reading JSON dataset from stdin"); + delete[] oldBuf; + return EC_MemoryExhausted; + } + if (oldBuf) + { + memmove(jsonString, oldBuf, bytesInBuffer); + delete[] oldBuf; + oldBuf = NULL; + } + writePtr = jsonString + bytesInBuffer; + bufFree = bufSize - bytesInBuffer; + readResult = fread(writePtr, 1, bufFree, stdin); + bytesInBuffer += readResult; + bufFree -= readResult; + if ((! feof(stdin)) && (bufFree == 0)) + { + // we need more buffer. Double the size. + oldBuf = jsonString; + jsonString = NULL; + bufSize *= 2; + } + else if (feof(stdin)) + { + jsonString[bytesInBuffer] = '\0'; + + // dataset was successfully read into buffer. Store the result, and in the process, + // clear the token array which is now invalid + clear(); + jsonDataset_ = jsonString; + jsonDatasetLen_ = bytesInBuffer; + return EC_Normal; + } + } while (OFTrue); +} + + +OFCondition DcmJSONReader::reserveTokens() +{ + if ((NULL == jsonDataset_) || (jsonDatasetLen_ < 2)) + return EC_IllegalCall; + + OFJsmnParser jsmnParser; + jsmn_init(&jsmnParser); + int tokenNum = jsmn_parse(&jsmnParser, jsonDataset_, jsonDatasetLen_, NULL, 0); + + if (tokenNum <= 0) + return EC_InvalidJSONContent; + + // delete old token array, if still present + tokenNumber_ = 0; + delete[] tokenArray_; + + // allocate one additional token + tokenArray_ = new (std::nothrow) OFJsmnToken[tokenNum+1]; + if (NULL == tokenArray_) + { + DCMDATA_ERROR("out of memory: failed to allocate JSON token array"); + return EC_MemoryExhausted; + } + + // initialize token array with zeroes + memset(tokenArray_, 0, tokenNum * sizeof(OFJsmnToken)); + + // fill dummy token at the end of the array with values + // that allow the loop in parseElement() to reliably terminate + tokenArray_[tokenNum].start = INT_MAX; + tokenArray_[tokenNum].end = INT_MAX; + tokenArray_[tokenNum].size = 0; + + // finally, store number of tokens + tokenNumber_ = tokenNum; + + DCMDATA_TRACE("JSMN tokens reserved: " << tokenNum); + return EC_Normal; +} + + +void DcmJSONReader::getTokenContent(OFString& value, OFJsmnTokenPtr t) +{ + int size = t->end - t->start; + + // remember the character immediately following the token + char c = jsonDataset_[t->start+size]; + + // convert the token string to a null terminated C string + jsonDataset_[t->start+size] = '\0'; + + // copy token string into output parameter + value = jsonDataset_ + t->start; + + // restore array + jsonDataset_[t->start+size] = c; + return; +} + + +OFCondition DcmJSONReader::dumpJSONTokenArray() +{ + if ((NULL == tokenArray_) || (tokenNumber_ < 1)) + return EC_IllegalCall; + + const char *json_type; + OFString json_value; + fprintf(stderr, "============================== BEGIN JSON DUMP ==============================\n"); + for (int i=0; i < tokenNumber_; ++i) + { + switch (tokenArray_[i].type) + { + case JSMN_OBJECT: + json_type = "object, size="; + break; + case JSMN_ARRAY: + json_type = "array, size="; + break; + case JSMN_STRING: + json_type = "string, size="; + break; + case JSMN_PRIMITIVE: + json_type = "primitive, size="; + break; + case JSMN_UNDEFINED: + default: + json_type = "undefined, size="; + break; + } + + getTokenContent(json_value, &tokenArray_[i]); + fprintf(stderr, "%06d: type=%s%04d, value=%s\n", i+1, json_type, tokenArray_[i].size, json_value.c_str()); + } + fprintf(stderr, "=============================== END JSON DUMP ===============================\n"); + return EC_Normal; +} + + +OFCondition DcmJSONReader::parseJSON() +{ + if ((NULL == tokenArray_) || (tokenNumber_ < 1)) + return EC_IllegalCall; + + OFJsmnParser jsmnParser; + jsmn_init(&jsmnParser); + + int parsRes = jsmn_parse(&jsmnParser, jsonDataset_, jsonDatasetLen_, tokenArray_, tokenNumber_); + + if (parsRes < 0) + { + // a parse error in jsmn_parse occurred + DCMDATA_ERROR("parse error in JSON file"); + if (parsRes == JSMN_ERROR_INVAL) + return EC_InvalidCharacter; + else return EC_InvalidJSONContent; + } + + return EC_Normal; +} + + +OFCondition DcmJSONReader::storeInlineBinaryValue( + DcmElement& element, + Uint8 *data, + size_t length) +{ + OFCondition result = EC_Normal; + if (NULL == data) length = 0; + DcmEVR evr = element.getVR(); + if (evr == EVR_OW) + { + /* Base64 decoder produces little endian output data, convert to local byte order */ + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, data, OFstatic_cast(Uint32, length), sizeof(Uint16)); + result = element.putUint16Array(OFreinterpret_cast(Uint16 *, data), OFstatic_cast(Uint32, length / sizeof(Uint16))); + } + else if (evr == EVR_OF) + { + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, data, OFstatic_cast(Uint32, length), sizeof(Float32)); + result = element.putFloat32Array(OFreinterpret_cast(Float32 *, data), OFstatic_cast(Uint32, length / sizeof(Float32))); + } + else if (evr == EVR_OD) + { + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, data, OFstatic_cast(Uint32, length), sizeof(Float64)); + result = element.putFloat64Array(OFreinterpret_cast(Float64 *, data), OFstatic_cast(Uint32, length / sizeof(Float64))); + } + else if (evr == EVR_OL) + { + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, data, OFstatic_cast(Uint32, length), sizeof(Uint32)); + result = element.putUint32Array(OFreinterpret_cast(Uint32 *, data), OFstatic_cast(Uint32, length / sizeof(Uint32))); + } + else if (evr == EVR_OV) + { + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, data, OFstatic_cast(Uint32, length), sizeof(Uint64)); + result = element.putUint64Array(OFreinterpret_cast(Uint64 *, data), OFstatic_cast(Uint32, length / sizeof(Uint64))); + } + else if (evr == EVR_OB || evr == EVR_UN) + { + result = element.putUint8Array(data, OFstatic_cast(Uint32, length)); + } + else + { + DCMDATA_ERROR("invalid VR Type for inline value"); + return EC_InvalidVR; + } + + if (result.bad()) + { + DCMDATA_ERROR("failed to store inline binary value for DICOM element " << element.getTag() << ": " << result.text()); + } + return result; +} + + +OFCondition DcmJSONReader::storeBulkValue( + DcmElement& element, + Uint8 *data, + size_t length) +{ + OFCondition result = EC_Normal; + if (NULL == data) length = 0; + DcmEVR evr = element.getVR(); + + if (evr == EVR_OB || evr == EVR_UN) + { + // sequence of bytes, no byte swapping necessary + result = element.putUint8Array(data, OFstatic_cast(Uint32, length)); + } + else if (evr == EVR_DS || evr == EVR_IS || evr == EVR_LT || evr == EVR_ST || evr == EVR_UT || evr == EVR_UC) + { + // sequence of bytes, no byte swapping necessary + result = element.putString(OFreinterpret_cast(char *, data), OFstatic_cast(Uint32, length)); + } + else if (evr == EVR_OW || evr == EVR_US) + { + // bulk data is always in little endian, convert to local byte order + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, data, OFstatic_cast(Uint32, length), sizeof(Uint16)); + result = element.putUint16Array(OFreinterpret_cast(Uint16 *, data), OFstatic_cast(Uint32, length / sizeof(Uint16))); + } + else if (evr == EVR_SS) + { + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, data, OFstatic_cast(Uint32, length), sizeof(Sint16)); + result = element.putSint16Array(OFreinterpret_cast(Sint16 *, data), OFstatic_cast(Uint32, length / sizeof(Sint16))); + } + else if (evr == EVR_OL || evr == EVR_UL) + { + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, data, OFstatic_cast(Uint32, length), sizeof(Uint32)); + result = element.putUint32Array(OFreinterpret_cast(Uint32 *, data), OFstatic_cast(Uint32, length / sizeof(Uint32))); + } + else if (evr == EVR_SL) + { + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, data, OFstatic_cast(Uint32, length), sizeof(Sint32)); + result = element.putSint32Array(OFreinterpret_cast(Sint32 *, data), OFstatic_cast(Uint32, length / sizeof(Sint32))); + } + else if (evr == EVR_OV || evr == EVR_UV) + { + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, data, OFstatic_cast(Uint32, length), sizeof(Uint64)); + result = element.putUint64Array(OFreinterpret_cast(Uint64 *, data), OFstatic_cast(Uint32, length / sizeof(Uint64))); + } + else if (evr == EVR_SV) + { + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, data, OFstatic_cast(Uint32, length), sizeof(Sint64)); + result = element.putSint64Array(OFreinterpret_cast(Sint64 *, data), OFstatic_cast(Uint32, length / sizeof(Sint64))); + } + else if (evr == EVR_FL || evr == EVR_OF) + { + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, data, OFstatic_cast(Uint32, length), sizeof(Float32)); + result = element.putFloat32Array(OFreinterpret_cast(Float32 *, data), OFstatic_cast(Uint32, length / sizeof(Float32))); + } + else if (evr == EVR_FD || evr == EVR_OD) + { + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, data, OFstatic_cast(Uint32, length), sizeof(Float64)); + result = element.putFloat64Array(OFreinterpret_cast(Float64 *, data), OFstatic_cast(Uint32, length / sizeof(Float64))); + } + else + { + DCMDATA_ERROR("invalid VR Type for bulk data"); + return EC_InvalidVR; + } + + if (result.bad()) + { + DCMDATA_ERROR("failed to store bulk data value for DICOM element " << element.getTag() << ": " << result.text()); + } + return result; +} + + +OFCondition DcmJSONReader::processJSONEscapeCharacters(OFString& value) +{ + /* + \" represents the quotation mark character (U+0022). + \\ represents the reverse solidus character (U+005C). + \/ represents the solidus character (U+002F). + \b represents the backspace character (U+0008). + \f represents the form feed character (U+000C). + \n represents the line feed character (U+000A). + \r represents the carriage return character (U+000D). + \t represents the character tabulation character (U+0009). + */ + OFString escapeSeq = "\"\\/bfnrt"; + + // find backslash + for (size_t backSlash = value.find('\\'); + backSlash < value.length(); + backSlash = value.find('\\', backSlash + 1)) + { + if (value.length() < backSlash + 2) + { + // the given string is shorter than expected. just return the string + DCMDATA_ERROR("incomplete JSON escape sequence"); + return EC_InvalidJSONContent; + } + char sigChar = value[backSlash + 1]; + OFString front = value.substr(0, backSlash); + OFString escString, back, replacement; + unsigned int unicodeCodepoint = 0; + + if (escapeSeq.find(sigChar) < escapeSeq.length()) + { + back = value.substr(backSlash + 2); + escString = sigChar; + + switch (sigChar) + { + case 'b': + unicodeCodepoint = 0x0008; + replacement = OFstatic_cast(char, unicodeCodepoint); + break; + case 'f': + unicodeCodepoint = 0x000C; + replacement = OFstatic_cast(char, unicodeCodepoint); + break; + case 'n': + unicodeCodepoint = 0x000A; + replacement = OFstatic_cast(char, unicodeCodepoint); + break; + case 'r': + unicodeCodepoint = 0x000D; + replacement = OFstatic_cast(char, unicodeCodepoint); + break; + case 't': + unicodeCodepoint = 0x0009; + replacement = OFstatic_cast(char, unicodeCodepoint); + break; + default: // \ / " + replacement = sigChar; + break; + } + } + else + { + // does not contain the full pattern of "\uXXXX" + if (sigChar != 'u' || value.length() < backSlash + 6) + { + // unknown escape sequence or incomplete unicode code point + DCMDATA_ERROR("unknown JSON escape sequence or incomplete Unicode code point"); + return EC_InvalidJSONContent; + } + + escString = value.substr(backSlash + 2, 4); + // parse escString to unicodeCodepoint + if (sscanf(escString.c_str(), "%x", &unicodeCodepoint) != 1) + { + // Invalid hex code + DCMDATA_ERROR("invalid hex code in JSON escape sequence"); + return EC_InvalidJSONContent; + } + + // convert Unicode codepoint to UTF-8 string + if (unicodeCodepoint < 0x80) + { + // UTF-8 sequence with one byte + replacement = OFstatic_cast(char, unicodeCodepoint); + back = value.substr(backSlash + 6); + } + else if (unicodeCodepoint < 0x800) + { + // UTF-8 sequence with two bytes + replacement = OFstatic_cast(char, ((unicodeCodepoint >> 6) & 0x1F) | 0xC0); + replacement += OFstatic_cast(char, ((unicodeCodepoint >> 0) & 0x3F) | 0x80); + back = value.substr(backSlash + 6); + } + else if ((unicodeCodepoint >= 0xD800) && ((unicodeCodepoint < 0xE000))) + { + // UTF-16 surrogate pair consisting two Unicode code points + if ((value.length() < backSlash + 12) || (value[backSlash + 6] != '\\') || (value[backSlash + 7] != 'u')) + { + DCMDATA_ERROR("invalid JSON UTF-16 surrogate pair escape sequence"); + return EC_InvalidJSONContent; + } + + escString = value.substr(backSlash + 8, 4); + unsigned int unicodeCodepoint2; + if (sscanf(escString.c_str(), "%x", &unicodeCodepoint2) != 1) + { + // Invalid hex code + DCMDATA_ERROR("invalid hex code in JSON escape sequence"); + return EC_InvalidJSONContent; + } + + // combine the two UTF-16 surrogates to a single Unicode code point. + // The first code point is the high surrogate (D800..DBFF), the second one the low surrogate (DC00..DFFF) + unicodeCodepoint = ((unicodeCodepoint - 0xD800) << 10) + unicodeCodepoint2 - 0xDC00 + 0x10000; + // UTF-8 sequence with four bytes + replacement = OFstatic_cast(char, ((unicodeCodepoint >> 18) & 0x07) | 0xF0); + replacement += OFstatic_cast(char, ((unicodeCodepoint >> 12) & 0x3F) | 0x80); + replacement += OFstatic_cast(char, ((unicodeCodepoint >> 6) & 0x3F) | 0x80); + replacement += OFstatic_cast(char, ((unicodeCodepoint >> 0) & 0x3F) | 0x80); + back = value.substr(backSlash + 12); + } + else + { + // UTF-8 sequence with three bytes + replacement = OFstatic_cast(char, ((unicodeCodepoint >> 12) & 0x0F) | 0xE0); + replacement += OFstatic_cast(char, ((unicodeCodepoint >> 6) & 0x3F) | 0x80); + replacement += OFstatic_cast(char, ((unicodeCodepoint >> 0) & 0x3F) | 0x80); + back = value.substr(backSlash + 6); + } + } + DCMDATA_TRACE("the escaped string [" << escString << "] is parsed to UTF-8:" + << replacement << " - unicode:" << std::hex << unicodeCodepoint); + value = front + replacement + back; + } + return EC_Normal; +} + + +OFCondition DcmJSONReader::parseElement( + DcmItem* dataset, + DcmItem* metaheader, + OFJsmnTokenPtr& current) +{ + OFCondition result = EC_Normal; + DCMDATA_TRACE("element at " << current->start); + + // the key for the element tag has to be a string + if (current->type != JSMN_STRING) + { + DCMDATA_ERROR("not a valid DICOM JSON dataset: element tag must be a JSON string"); + return EC_InvalidJSONType; + } + + DcmTagKey tagkey; + result = extractTag(current, tagkey); + if (result.bad()) + { + if (stopOnErrorPolicy_) return result; else result = EC_Normal; + } + DcmTag dcmTag(tagkey); + + // the next token contains the whole element encapsulated into a JSON object. + current++; + if (current->type != JSMN_OBJECT) + { + DCMDATA_ERROR("not a valid DICOM JSON dataset: element content must be a JSON object"); + return EC_InvalidJSONType; + } + + // number of attributes for the element + int contentSize = current->size; + DcmElement* newElem = NULL; + current++; + + // examine the attributes + OFJsmnTokenPtr vrToken = NULL, valueToken = NULL; + OFString valueType; + for (int count = 0; count < contentSize; count++) + { + OFString attrName; + getTokenContent(attrName, current); + attrName = OFStandard::toLower(attrName); + DCMDATA_TRACE("attribute '" << attrName << "' token at " << current->start << " - " << current->end); + current++; + + if (attrName == "vr") + { + if (vrToken == NULL) + vrToken = current; + else + DCMDATA_WARN("attribute '" << attrName << " already present in this JSON object. This token will be ignored"); + } + else + { + if (attrName == "bulkdatauri" || attrName == "inlinebinary" || attrName == "value") + { + if (valueToken == NULL) + { + valueToken = current; + valueType = attrName; + } + else + DCMDATA_WARN("attribute '" << attrName << " already present in this JSON object. This token will be ignored"); + } + else + { + DCMDATA_ERROR("unknown JSON attribute name \"" << attrName << "\""); + return EC_InvalidJSONContent; + } + } + // find the next token in the current hierarchie + OFJsmnTokenPtr tmpToken = current; + while (current->start < tmpToken->end) + current++; + } + + OFString vr = ""; + if (vrToken != NULL) + getTokenContent(vr, vrToken); + + // create DICOM element + result = createElement(newElem, dcmTag, vr); + if (result.bad()) + { + if (stopOnErrorPolicy_) return result; else result = EC_Normal; + } + + if (valueToken == NULL) + { + // no content following, element value remains empty + DCMDATA_TRACE("no value token for element " << dcmTag << ", using empty value"); + } + + // bulk data URIs reference a file, download URL or a URN referencing another MIME part in multipart/related structure. + // This is not yet supported + else if (valueType == "bulkdatauri") + { + if (ignoreBulkdataURIPolicy_) + { + // leave the element with BulkdataURI empty + DCMDATA_INFO("ignoring BulkdataURI for element: " << dcmTag << ", leaving element empty"); + } + else + { + // the URI value has to be a JSON string + if (valueToken->type != JSMN_STRING) + { + DCMDATA_ERROR("not a valid DICOM JSON dataset: BulkdataURI value must be a JSON string"); + delete newElem; + return EC_InvalidJSONType; + } + + OFString value; + getTokenContent(value, valueToken); + + if (isFileURI(value)) + { + // convert URI to file path + OFString filePath; + OFString filePathNormalized; + size_t offset = 0; + size_t length = 0; + result = fileURItoPath(value, filePath, offset, length); + if (result.bad()) + { + delete newElem; + return result; + } + + // normalize file path + result = normalizePath(filePath, filePathNormalized); + if (result.bad()) + { + delete newElem; + return result; + } + + // check if file path is present in our list of permitted paths + if (! bulkdataPathPermitted(filePathNormalized)) + { + DCMDATA_ERROR("BulkdataURI refers to a directory that is not permitted for bulk data: '" << filePath << "'"); + delete newElem; + return EC_InvalidFilename; + } + + // read the file content and insert it into the current element + result = loadBulkdataFile(*newElem, filePathNormalized, offset, length); + if (result.bad()) + { + delete newElem; + return result; + } + } + else if (isHttpURI(value)) + { + DCMDATA_ERROR("loading Bulkdata from http/https BulkDataURI not yet possible"); + delete newElem; + return EC_UnsupportedURIType; + } + else + { + DCMDATA_ERROR("Unsupported BulkDataURI URI type: '" << value << "'"); + delete newElem; + return EC_UnsupportedURIType; + } + } + } + + // inlinebinary - content is base64 encoded + else if (valueType == "inlinebinary") + { + // the base64 value has to be a JSON string + if (valueToken->type != JSMN_STRING) + { + DCMDATA_ERROR("not a valid DICOM JSON dataset: InlineBinary value must be a JSON string"); + delete newElem; + return EC_InvalidJSONType; + } + + OFString value; + getTokenContent(value, valueToken); + Uint8* data = NULL; + const size_t length = OFStandard::decodeBase64(value, data); + DCMDATA_TRACE("parsing inline binary (" << length << "): " << value << " | " << data); + if (length > 0) + result = storeInlineBinaryValue(*newElem, data, length); + + /* delete buffer since data is copied into the element */ + delete[] data; + if (result.bad()) + { + if (stopOnErrorPolicy_) return result; else result = EC_Normal; + } + } + else if (valueType == "value") + { + if (valueToken->type != JSMN_ARRAY) + { + // the value of the element has to be an array + DCMDATA_ERROR("not a valid DICOM JSON dataset: attribute value must be a JSON array"); + delete newElem; + return EC_InvalidJSONType; + } + + // Sequence + if (newElem->ident() == EVR_SQ) + { + result = parseSequence(*(OFstatic_cast(DcmSequenceOfItems*, newElem)), valueToken); + } + else if (newElem->getTag() == DCM_PixelData) + { + // special handling for pixel data + DCMDATA_ERROR("pixel data must not have a 'value' attribute in the DICOM JSON model"); + delete newElem; + return EC_InvalidJSONContent; + } + else + { + // parse the value array + DCMDATA_TRACE("parsing value array of size " << valueToken->size); + result = parseElementValueArray(newElem, valueToken); + } + if (result.bad()) + { + if (stopOnErrorPolicy_) return result; else result = EC_Normal; + } + } + else + { + DCMDATA_ERROR("unknown JSON attribute name: " << valueType); + delete newElem; + return EC_InvalidJSONContent; + } + + if (result.good()) + { + // insert the new attribute to the metaheader if the tag is (0002,xxxx), otherwise to the dataset + if (dcmTag.getGroup() == 0x0002) + { + if (ignoreMetaInfoPolicy_ || (metaheader == NULL)) + { + // we ignore meta info elements + delete newElem; + } + else + { + if (dcmTag.getElement() == 0x0010) + { + // this is (0002,0010) TransferSyntaxUID, extract the transfer syntax + OFString value; + newElem->getOFString(value, 0); + xferSyntax_ = DcmXfer(value.c_str()).getXfer(); + } + result = metaheader->insert(newElem, OFFalse /*replaceOld*/); + } + } + else + { + result = dataset->insert(newElem, OFFalse /*replaceOld*/); + } + if (result.bad()) + { + DCMDATA_WARN("element " << dcmTag << " found twice in one data set or item, ignoring second entry"); + delete newElem; + } + } + else // result.bad() + { + /* delete element if insertion or putting the value failed */ + delete newElem; + } + return result; +} + + +OFCondition DcmJSONReader::extractTag( + OFJsmnTokenPtr keyToken, + DcmTagKey& tagkey) +{ + OFString tagString; + getTokenContent(tagString, keyToken); + if (tagString.empty() || tagString.size() != 8) + { + DCMDATA_ERROR("not a valid DICOM JSON dataset: expected attribute tag string with 8 characters, found '" << tagString << "'"); + return EC_InvalidTag; + } + unsigned long group, element; + OFString gStr = tagString.substr(0, 4); + OFString eStr = tagString.substr(4); + if (sscanf(gStr.c_str(), "%lx", &group) != 1 + || sscanf(eStr.c_str(), "%lx", &element) != 1) + { + DCMDATA_ERROR("not a valid DICOM JSON dataset: attribute tag must consist of two 16-bit hex numbers"); + return EC_InvalidTag; + } + + tagkey.set(OFstatic_cast(Uint16, group), OFstatic_cast(Uint16, element)); + return EC_Normal; +} + + +OFCondition DcmJSONReader::parseSequence( + DcmSequenceOfItems& sequence, + OFJsmnTokenPtr& current) +{ + OFCondition result = EC_Normal; + int sqSize = current->size; + int sqStart = current->start; + + DCMDATA_TRACE("sequence start: " << sqStart << " with size: " << sqSize); + current++; + + // iterate over sequence items + for (int i = 0; i < sqSize; i++) + { + DCMDATA_TRACE("item " << sqStart << ":" << i << " -- " << current->start); + + // sequence items have to be an object + if (current->type != JSMN_OBJECT) + return EC_InvalidJSONType; + + // create new sequence item + DcmItem* newItem = new DcmItem(); + if (newItem != NULL) + { + sequence.insert(newItem); + + // proceed parsing the item content + result = parseDataSet(newItem, NULL, current); + if (result.bad() && stopOnErrorPolicy_) return result; + } + DCMDATA_TRACE("item " << sqStart << ":" << i << " end, next up : " << current->start); + } + DCMDATA_TRACE("sequence end: " << sqStart << "; next element: " << current->start); + return result; +} + + +OFCondition DcmJSONReader::parseDataSet( + DcmItem* dataset, + DcmItem* metaheader, + OFJsmnTokenPtr& current) +{ + OFCondition result = EC_Normal; + + // we expext a JSON object that encapsulates the DICOM dataset + if (current->type != JSMN_OBJECT) + { + DCMDATA_ERROR("not a valid DICOM JSON dataset: datasets must be encapsulated in a JSON object"); + return EC_InvalidJSONType; + } + int dsSize = current->size; + int dsStart = current->start; + + DCMDATA_TRACE("dataset start " << dsStart << " - size: " << dsSize); + current++; + for (int i = 0; i < dsSize; i++) + { + // read each entry in the content object as a DICOM element + result = parseElement(dataset, metaheader, current); + if (result.bad() && stopOnErrorPolicy_) return result; + } + DCMDATA_TRACE("dataset end " << dsStart << "; next element: " << current->start); + return result; +} + + + + +OFCondition DcmJSONReader::parsePersonName( + OFString& value, + OFJsmnTokenPtr& current) +{ + static const char *PersonGroupNames[] = { "Alphabetic", "Ideographic", "Phonetic" }; + + OFCondition result; + int size = current->size; + if (size > 3) + { + DCMDATA_ERROR("not a valid DICOM JSON dataset: a person name must have at most three component groups"); + return EC_InvalidJSONType; + } + + OFVector pn(3); + for (int i = 0; i < size; i++) + { + current++; + if (current->type != JSMN_STRING) + { + DCMDATA_ERROR("not a valid DICOM JSON dataset: PN values must be JSON strings"); + return EC_InvalidJSONType; + } + + OFString key; + getTokenContent(key, current); + int idx = -1; + for (int j = 0; j < 3; j++) + { + if (key == PersonGroupNames[j]) + { + idx = j; + break; + } + } + if (idx < 0) + { + + DCMDATA_ERROR("not a valid DICOM JSON dataset: unsupported PN component group type '" << key << "'"); + return EC_InvalidJSONType; + } + + // if pn[idx] is not empty, it will be overwritten. + current++; + if (current->type != JSMN_STRING) + { + DCMDATA_ERROR("not a valid DICOM JSON dataset: PN values must be JSON strings"); + return EC_InvalidJSONType; + } + + getTokenContent(pn[idx], current); + + DCMDATA_TRACE("person name (PN) with " << key << " val " << pn[idx]); + result = processJSONEscapeCharacters(pn[idx]); + if (result.bad()) + { + if (stopOnErrorPolicy_) return result; else result = EC_Normal; + } + } + + // PN format is: "alphPN=ideoPN=phonPN" + if (!pn[0].empty()) + value += pn[0]; + if (!pn[1].empty() || !pn[2].empty()) + value += '=' + pn[1]; + if (!pn[2].empty()) + value += '=' + pn[2]; + DCMDATA_TRACE("PN value " << value); + return result; +} + + +OFCondition DcmJSONReader::parseElementValueArray( + DcmElement*& newElem, + OFJsmnTokenPtr& current) +{ + OFCondition result; + OFString vmString; + OFString value; + int vm = current->size; + + for (int count = 0; count < vm; count++) + { + value.clear(); + current++; + if (newElem->ident() == EVR_PN) + { + // special handling for person names (PN) + OFString tokenValue; + getTokenContent(tokenValue, current); + DCMDATA_TRACE("element value array, parsing PN value: " << tokenValue); + if (current->type != JSMN_OBJECT) + { + if (tokenValue == "null") + { + value = ""; + } + else + { + DCMDATA_ERROR("not a valid DICOM JSON dataset: PN components must be JSON strings or null"); + return EC_InvalidJSONType; + } + } + else + { + result = parsePersonName(value, current); + if (result.bad()) + { + if (stopOnErrorPolicy_) return result; else result = EC_Normal; + } + } + if (count > 0) + vmString += '\\'; + vmString += value; + } + else if (newElem->ident() == EVR_AT) + { + // special handling for attribute tags (AT). + // DcmAttributeTag::putOFStringArray() expects a format like this: "(0008,0020)\(0008,0030)" + if (current->type != JSMN_STRING) + { + DCMDATA_ERROR("not a valid DICOM JSON dataset: AT values must be JSON strings"); + return EC_InvalidJSONType; + } + + DcmTagKey tagkey; + result = extractTag(current, tagkey); + if (result.bad()) + { + if (stopOnErrorPolicy_) return result; else result = EC_Normal; + } + DCMDATA_TRACE("element value array, parsing AT value: " << tagkey.toString()); + + if (count > 0) + vmString += '\\'; + vmString += tagkey.toString(); + } + else + { + // default for all other VRs (numeric and text) + if (current->type != JSMN_PRIMITIVE && current->type != JSMN_STRING) + { + DCMDATA_ERROR("not a valid DICOM JSON dataset: expected number, string or null"); + return EC_InvalidJSONType; + } + + getTokenContent(value, current); + if (current->type == JSMN_STRING) + { + // JSMN_STRING + DCMDATA_TRACE("element value array, parsing string value: " << value); + result = processJSONEscapeCharacters(value); + if (result.bad()) + { + if (stopOnErrorPolicy_) return result; else result = EC_Normal; + } + } + else + { + // JSMN_PRIMITIVE, i.e. null, number, or boolean (which should not occur) + // Replace "null" by an empty string and keep numbers as they are + if (value == "null") value = ""; + } + if (count > 0) + vmString += '\\'; + vmString += value; + DCMDATA_TRACE("element value array: all values: " << vmString); + } + } + + result = newElem->putOFStringArray(vmString); + if (result.bad()) + { + DCMDATA_ERROR("failed to store string value for element " << newElem->getTag() << ": " << result.text()); + } + return result; +} + + + + +OFCondition DcmJSONReader::createElement( + DcmElement*& newElem, + DcmTag& dcmTag, + const OFString& vr) +{ + OFCondition result = EC_Normal; + + DCMDATA_TRACE("parsing VR: " << vr); + + // convert vr string + const DcmVR jsonVR(vr.c_str()); + if (jsonVR.isUnknown() || jsonVR.isInvalid()) + { + // check whether "vr" attribute exists + if (vr.empty() || vr == "") + { + DCMDATA_WARN("missing 'vr' attribute for " << dcmTag + << ", using the tag's VR (" << dcmTag.getVR().getVRName() << ")"); + } + else { + DCMDATA_WARN("invalid 'vr' attribute (" << vr << ") for " << dcmTag + << ", using the tag's VR (" << dcmTag.getVR().getVRName() << ")"); + } + } + else + { + const DcmEVR dcmEVR = jsonVR.getEVR(); + const DcmEVR dictionaryEVR = dcmTag.getEVR(); + + // Check if the VR is correct and print a warning otherwise. + // + // Normally, the JSON file and the data dictionary should specify the same + // VR for a tag (i.e., dictionaryEVR == dcmEVR), or dictionaryEVR is EVR_UNKNOWN + // if the tag is not present in our data dictionary. + + if ((dictionaryEVR != dcmEVR) && (dictionaryEVR != EVR_UNKNOWN) && + // LUTData VR can be US, SS or OW + ((dcmTag.getTagKey() != DCM_LUTData) || ((dcmEVR != EVR_US) && (dcmEVR != EVR_SS) && (dcmEVR != EVR_OW))) && + // If the dictionary says "EVR_xs", the VR can either be US or SS + ((dictionaryEVR != EVR_xs) || ((dcmEVR != EVR_US) && (dcmEVR != EVR_SS))) && + // If the dictionary says "EVR_ox" (OB/OW) or "EVR_px" (pixel data), the VR can either be OB or OW + (((dictionaryEVR != EVR_ox) && (dictionaryEVR != EVR_px)) || ((dcmEVR != EVR_OB) && (dcmEVR != EVR_OW)))) + { + // there are inconsistencies concerning the VR in the JSON file. + // print a warning since there may be resulting errors in the DICOM dataset + DCMDATA_WARN("element " << dcmTag << " has wrong VR (" << jsonVR.getVRName() + << "), correct is '" << dcmTag.getVR().getVRName() << "'"); + } + + // change the VR to the one specified in the JSON dataset, even if it does not match our dictionary + dcmTag.setVR(jsonVR); + } + + // create DICOM element with given tag and VR + result = DcmItem::newDicomElementWithVR(newElem, dcmTag); + if (result.bad()) + { + DCMDATA_ERROR("failed to create DICOM element " << dcmTag << ": " << result.text()); + } + + // if this is pixel data, create an empty unencapsulated representation + // to make sure that writing the element with empty value will not fail + if (dcmTag == DCM_PixelData) newElem->putUint16Array(NULL,0); + return result; +} + + +OFCondition DcmJSONReader::readAndConvertJSONFile( + DcmFileFormat& fileformat, + const char *ifname) +{ + // clear old buffers, in case this object is re-used + clear(); + DcmMetaInfo* metaheader = fileformat.getMetaInfo(); + DcmDataset* dataset = fileformat.getDataset(); + OFCondition result; + + // readin the input file to a memory buffer + OFString stdinName("-"); + if (ifname == stdinName) + result = readJSONFromStdin(); + else result = readJSONFile(ifname); + if (result.bad()) return result; + + // calculate tokens needed for the parser and allocate tokens + result = reserveTokens(); + if (result.bad()) return result; + + // use the JSON library to parse the string and save it to the token array. + result = parseJSON(); + if (result.bad() && stopOnErrorPolicy_) return result; + + // check if the token array starts with a JSON array or a JSON object + OFJsmnTokenPtr current = tokenArray_; + if (current->type == JSMN_ARRAY) + { + if (current->size < 1) + { + DCMDATA_ERROR("found empty JSON array instead of DICOM JSON dataset"); + return EC_InvalidJSONContent; + } + if (current->size == 1) + { + // this is a JSON array containing a single DICOM dataset. + // Silently ignore the array structure and parse the dataset + DCMDATA_DEBUG("parsing JSON array containing a single dataset"); + current++; + result = parseDataSet(dataset, metaheader, current); + } + else + { + // this is a JSON array containing a multiple DICOM datasets. + if (arrayHandlingPolicy_ < 0) + { + // reject multiple datasets + DCMDATA_ERROR("found JSON array containing " << current->size << " DICOM datasets, rejecting conversion"); + result = EC_InvalidJSONContent; + } + else if (arrayHandlingPolicy_ == 0) + { + // Store multiple datasets in a private sequence. + DCMDATA_DEBUG("parsing JSON array containing " << current->size << " DICOM datasets"); + DcmTag private_reservation(0x0009,0x0010, EVR_LO); + DcmTag private_sequence(0x0009,0x1000, EVR_SQ); + DcmSequenceOfItems *newSQ = new DcmSequenceOfItems(private_sequence); + result = dataset->putAndInsertString(private_reservation, JSON2DCM_PRIVATE_RESERVATION); + if (result.good()) result = dataset->insert(newSQ); + if (result.good()) result = parseSequence(*newSQ, current); + } + else + { + // select a single dataset from the array + if (arrayHandlingPolicy_ > current->size) + { + DCMDATA_ERROR("found JSON array containing " << current->size << " DICOM datasets, cannot store dataset no. " << arrayHandlingPolicy_); + result = EC_InvalidJSONContent; + } + else + { + DCMDATA_DEBUG("selecting dataset " << arrayHandlingPolicy_ << " from JSON array containing " << current->size << " DICOM datasets"); + + // move "current" to the token representing the first dataset + current++; + + // number of datasets to skip + signed long tokensToSkip = arrayHandlingPolicy_ - 1; + + // recursively skip tokens including their sub-tokens until we are done + while (tokensToSkip > 0) + { + tokensToSkip += current->size; + current++; + tokensToSkip--; + } + + // extract the single dataset at the target location + result = parseDataSet(dataset, metaheader, current); + } + } + } + } + else + { + // we expect a single dataset here, parseDataSet() will check if it is the right JSON structure + DCMDATA_DEBUG("parsing single JSON dataset"); + result = parseDataSet(dataset, metaheader, current); + } + + if (!stopOnErrorPolicy_) result = EC_Normal; + return result; +} + + +OFBool DcmJSONReader::isFileURI(const OFString& uri) const +{ + return (uri.substr(0, 6) == "file:/"); +} + + +OFBool DcmJSONReader::isHttpURI(const OFString& uri) const +{ + OFString s = uri.substr(0, 8); + if (s == "https://") return OFTrue; + s.erase(7); + return (s == "http://"); +} + + +OFCondition DcmJSONReader::urlDecode(OFString& uri) const +{ + size_t pos; + unsigned int val = 0; + char c; + while (OFString_npos != (pos = uri.find("%"))) + { + if (uri.length() + 3 < pos) + { + DCMDATA_ERROR("incomplete URL code: " << uri.substr(pos, 3)); + return EC_UnsupportedURIType; + + } + c = uri[pos+1]; + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) + { + DCMDATA_ERROR("invalid URL code: " << uri.substr(pos, 3)); + return EC_UnsupportedURIType; + } + + c = uri[pos+2]; + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) + { + DCMDATA_ERROR("invalid URL code: " << uri.substr(pos, 3)); + return EC_UnsupportedURIType; + } + + if (1 != sscanf(uri.c_str() + pos, "%%%2x", &val)) + { + DCMDATA_ERROR("invalid URL code: " << uri.substr(pos, 3)); + return EC_UnsupportedURIType; + } + uri[pos] = OFstatic_cast(char, val); + uri.erase(pos+1, 2); + } + return EC_Normal; +} + + +OFCondition DcmJSONReader::fileURItoPath(const OFString& uri, OFString& filepath, size_t& offset, size_t& length) const +{ + // clear output parameters + filepath.clear(); + offset = 0; + length = 0; + + // check if this is a file URI at all + if ((uri.substr(0, 6) != "file:/") || (uri.length() < 7)) + { + DCMDATA_ERROR("not a file URI: " << uri); + return EC_UnsupportedURIType; + } + + OFString filePath; + OFString params; + + // check if we have URI parameters. If they are present, store them in a separate string. + // In any case, remove the "file:" prefix from the path. + size_t paramStart = uri.find("?"); + if (paramStart == OFString_npos) + { + filePath = uri.substr(5); + } + else + { + params = uri.substr(paramStart+1); + filePath = uri.substr(5, paramStart-5); + } + + OFCondition result = urlDecode(params); + if (result.bad()) return result; + + result = urlDecode(filePath); + if (result.bad()) return result; + + // check if we have a URI of type "file:/path" or "file://host/path" + if (filePath[1] == '/') // we have checked earlier that the URI is long enough for this + { + // URI type: file://host/path + if (filePath.substr(1, 3) == "///") + { + DCMDATA_ERROR("Access to file URIs for network hosts not supported: " << uri); + return EC_UnsupportedURIType; + } + + // determine length of hostname + size_t separator = filePath.find('/', 2); + if (separator == OFString_npos) + { + DCMDATA_ERROR("file URI without path not supported: " << uri); + return EC_UnsupportedURIType; + } + + // separate hostname and path + OFString host = filePath.substr(2, separator-2); + filePath = filePath.substr(separator); + if ((host != "") && (host != "localhost") && (host != "127.0.0.1") && (host != "ip6-localhost") && (host != "::1")) + { + DCMDATA_ERROR("Access to file URIs for network hosts not supported: " << uri); + return EC_UnsupportedURIType; + } + } + if (filePath.length() < 2) + { + DCMDATA_ERROR("file URI without path not supported: " << uri); + return EC_UnsupportedURIType; + } + +#ifdef HAVE_WINDOWS_H + if ((filePath.length() > 2) && (filePath[2] == ':')) + { + // first component of the path is a drive name. Remove leading slash. + // We don't do this on Posix systems because the colon character is permitted there as a filename component. + filePath.erase(0,1); + } +#endif + + // replace '/' in the path by the system specific path separator + size_t l = filePath.size(); + for (size_t i = 0; i < l; ++i) + { + if (filePath[i] == '/') filePath[i] = PATH_SEPARATOR; + } + + // now for the URI parameters... + size_t paramSep; + OFString currentParam; + while (params.length() > 0) + { + // separate the next parameter + paramSep = params.find("&"); + if (paramSep == OFString_npos) + { + currentParam = params; + params = ""; + } + else + { + currentParam = params.substr(0, paramSep); + params = params.substr(paramSep + 1); + } + if (currentParam.substr(0,7) == "offset=") + { + // convert offset + unsigned long ll = 0; + currentParam.erase(0,7); + if ((OFString_npos != currentParam.find_first_not_of("0123456789")) || (1 != sscanf(currentParam.c_str(), "%lu", &ll))) + { + DCMDATA_ERROR("Invalid value for URI parameter 'offset': " << currentParam); + return EC_UnsupportedURIType; + } + offset = OFstatic_cast(size_t, ll); + } + else if (currentParam.substr(0,7) == "length=") + { + // convert length + unsigned long ll = 0; + currentParam.erase(0,7); + if ((OFString_npos != currentParam.find_first_not_of("0123456789")) || (1 != sscanf(currentParam.c_str(), "%lu", &ll))) + { + DCMDATA_ERROR("Invalid value for URI parameter 'length': " << currentParam); + return EC_UnsupportedURIType; + } + length = OFstatic_cast(size_t, ll); + } + else + { + DCMDATA_ERROR("'file' URI with unsupported parameter '" << currentParam << "': " << uri); + return EC_UnsupportedURIType; + } + } + filepath = filePath; + return EC_Normal; +} + + +OFCondition DcmJSONReader::normalizePath(const OFString& filepath_in, OFString& filepath_out) const +{ + filepath_out.clear(); +#ifdef HAVE_WINDOWS_H + char buf[32768]; + + // resolve a relative path to an absolute path + DWORD res = GetFullPathNameA(filepath_in.c_str(), 32768, buf, NULL); + if ((res == 0) || (res > 32768)) + { + DCMDATA_ERROR("Failed to normalize file path: '" << filepath_in << "'"); + return EC_InvalidFilename; + } + + // resolve short file and directory name components such as "PROGRA~1" + // (which is the short 8.3 version for "Program Files") into the long names + res = GetLongPathNameA(buf, buf, 32768); + if ((res == 0) || (res > 32768)) + { + DCMDATA_ERROR("Failed to normalize file path: '" << filepath_in << "'"); + return EC_InvalidFilename; + } + + // convert all characters to uppercase using a function that (hopefully) + // uses the same mapping table as the WIN32 file API + size_t len = strlen(buf); + if (len != CharUpperBuffA(buf, OFstatic_cast(DWORD, len))) + { + DCMDATA_ERROR("Failed to normalize file path: '" << filepath_in << "'"); + return EC_InvalidFilename; + } + filepath_out = buf; +#else + // resolve a relative path to an absolute path without symbolic links + char *resolved_path = realpath(filepath_in.c_str(), NULL); + if (resolved_path == NULL) + { + DCMDATA_ERROR("Failed to normalize file path: '" << filepath_in << "'"); + return EC_InvalidFilename; + } + filepath_out = resolved_path; + free(resolved_path); +#endif + return EC_Normal; +} + + +OFCondition DcmJSONReader::addPermittedBulkdataPath(const OFString& dirpath) +{ + OFString std_dirpath; + OFCondition result = normalizePath(dirpath, std_dirpath); + if ((std_dirpath.length() > 0) && (std_dirpath[std_dirpath.length()-1] != PATH_SEPARATOR)) + { + std_dirpath.append(1, PATH_SEPARATOR); + } + + if (result.good()) permittedBulkdataDirs_.push_back(std_dirpath); + return result; + +} + + +OFBool DcmJSONReader::bulkdataPathPermitted(const OFString& filepath) const +{ + OFListConstIterator(OFString) iter = permittedBulkdataDirs_.begin(); + OFListConstIterator(OFString) last = permittedBulkdataDirs_.end(); + while (iter != last) + { + if (filepath.substr(0, (*iter).length()) == *iter) return OFTrue; + ++iter; + } + return OFFalse; +} + + +OFCondition DcmJSONReader::loadBulkdataFile( + DcmElement& element, + const OFString& filepath, + size_t offset, + size_t length) +{ + // open file for reading + OFFile file; + if (! file.fopen(filepath, "rb")) + { + OFString s("(unknown error code)"); + file.getLastErrorString(s); + return makeOFCondition(OFM_dcmdata, 18, OF_error, s.c_str()); + } + + // obtain file size and check if file is large enough + const size_t filelen = OFStandard::getFileSize(filepath); + if (0 == length) length = filelen; + if (offset + length > filelen) + { + DCMDATA_ERROR("bulk data file too short: '" << filepath << "', expected " << offset + length << " bytes but only found " << filelen); + return EC_EndOfStream; + } + + // allocate buffer + Uint8 *bulkDataBuffer = new (std::nothrow) Uint8[length]; + if (bulkDataBuffer == NULL) + { + DCMDATA_ERROR("out of memory: failed to allocate buffer for bulk data file"); + return EC_MemoryExhausted; + } + + // seek to the given offset within the file + if (offset > 0) + { + if (0 != file.fseek(offset, SEEK_SET)) + { + OFString s("(unknown error code)"); + file.getLastErrorString(s); + delete[] bulkDataBuffer; + return makeOFCondition(OFM_dcmdata, 18, OF_error, s.c_str()); + } + } + + // read the bulk data into the buffer + size_t res = file.fread(bulkDataBuffer, 1, length); + + // we ignore the fclose() return code, which is safe since the file is read-only + file.fclose(); + + // check the number of bytes read + if (res != length) + { + OFString s("(unknown error code)"); + file.getLastErrorString(s); + delete[] bulkDataBuffer; + return makeOFCondition(OFM_dcmdata, 18, OF_error, s.c_str()); + } + + // file was successfully read into buffer. Store the result. + OFCondition result = storeBulkValue(element, bulkDataBuffer, length); + delete[] bulkDataBuffer; + return result; +} diff --git a/dcmdata/libsrc/dclist.cc b/dcmdata/libsrc/dclist.cc index 3104da27..26332cbc 100644 --- a/dcmdata/libsrc/dclist.cc +++ b/dcmdata/libsrc/dclist.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2019, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -22,6 +22,7 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "dcmtk/ofstd/ofstream.h" +#include "dcmtk/ofstd/oflimits.h" #include "dcmtk/dcmdata/dclist.h" @@ -30,7 +31,7 @@ // ***************************************** -DcmListNode::DcmListNode( DcmObject *obj ) +DcmListNode::DcmListNode(DcmObject *obj) : nextNode(NULL), prevNode(NULL), objNodeValue(obj) @@ -51,10 +52,18 @@ DcmListNode::~DcmListNode() // ***************************************** +// value should be identical to DCM_EndOfListIndex, e.g. 0xffffffff for 32 bit +static const unsigned long invalidListPosition = OFnumeric_limits::max(); + + +// ******************************** + + DcmList::DcmList() : firstNode(NULL), lastNode(NULL), currentNode(NULL), + currentPosition(invalidListPosition), cardinality(0) { } @@ -65,16 +74,17 @@ DcmList::DcmList() DcmList::~DcmList() { - if ( !DcmList::empty() ) // list is not empty ! + if (!DcmList::empty()) { - lastNode->nextNode = NULL; // set to 0 for safety reasons + lastNode->nextNode = NULL; // set to 0 for safety reasons do { DcmListNode *temp = firstNode; firstNode = firstNode->nextNode; - // delete temp->objNodeValue; // dangerous! + // delete temp->objNodeValue; // dangerous! delete temp; - } while ( firstNode != NULL ); + } while (firstNode != NULL); currentNode = firstNode = lastNode = NULL; + currentPosition = invalidListPosition; } } @@ -82,20 +92,29 @@ DcmList::~DcmList() // ******************************** -DcmObject *DcmList::append( DcmObject *obj ) +DcmObject *DcmList::append(DcmObject *obj) { - if ( obj != NULL ) + if (obj != NULL) { - if ( DcmList::empty() ) // list is empty ! + if (DcmList::empty()) + { currentNode = firstNode = lastNode = new DcmListNode(obj); - else + currentPosition = cardinality; + cardinality++; + } + // check whether object can be inserted + else if (cardinality < DCM_EndOfListIndex) { DcmListNode *node = new DcmListNode(obj); lastNode->nextNode = node; node->prevNode = lastNode; currentNode = lastNode = node; + currentPosition = cardinality; + cardinality++; + } else { + DCMDATA_DEBUG("DcmList::append() cannot insert object, maximum number of entries reached"); + obj = NULL; } - cardinality++; } // obj == NULL return obj; } @@ -104,20 +123,29 @@ DcmObject *DcmList::append( DcmObject *obj ) // ******************************** -DcmObject *DcmList::prepend( DcmObject *obj ) +DcmObject *DcmList::prepend(DcmObject *obj) { - if ( obj != NULL ) + if (obj != NULL) { - if ( DcmList::empty() ) // list is empty ! + if (DcmList::empty()) + { currentNode = firstNode = lastNode = new DcmListNode(obj); - else + currentPosition = 0; + cardinality++; + } + // check whether object can be inserted + else if (cardinality < DCM_EndOfListIndex) { DcmListNode *node = new DcmListNode(obj); node->nextNode = firstNode; firstNode->prevNode = node; currentNode = firstNode = node; + currentPosition = 0; + cardinality++; + } else { + DCMDATA_DEBUG("DcmList::prepend() cannot insert object, maximum number of entries reached"); + obj = NULL; } - cardinality++; } // obj == NULL return obj; } @@ -126,51 +154,59 @@ DcmObject *DcmList::prepend( DcmObject *obj ) // ******************************** -DcmObject *DcmList::insert( DcmObject *obj, E_ListPos pos ) +DcmObject *DcmList::insert(DcmObject *obj, const E_ListPos pos) { - if ( obj != NULL ) + if (obj != NULL) { - if ( DcmList::empty() ) // list is empty ! + if (DcmList::empty()) { currentNode = firstNode = lastNode = new DcmListNode(obj); + currentPosition = 0; cardinality++; } - else { - if ( pos==ELP_last ) - DcmList::append( obj ); // cardinality++; - else if ( pos==ELP_first ) - DcmList::prepend( obj ); // cardinality++; - else if ( !DcmList::valid() ) + // check whether object can be inserted + else if (cardinality < DCM_EndOfListIndex) + { + if (pos == ELP_last) // insert at the end + DcmList::append(obj); + else if (pos == ELP_first) // insert at the beginning + DcmList::prepend(obj); + else if (!DcmList::valid()) // set current node to the end if there is no predecessor or // there are successors to be determined - DcmList::append( obj ); // cardinality++; - else if ( pos == ELP_prev ) // insert before current node + DcmList::append(obj); + else if (pos == ELP_prev) // insert before current node { DcmListNode *node = new DcmListNode(obj); - if ( currentNode->prevNode == NULL ) - firstNode = node; // insert at the beginning + if (currentNode->prevNode == NULL) + firstNode = node; // insert at the beginning else currentNode->prevNode->nextNode = node; node->prevNode = currentNode->prevNode; node->nextNode = currentNode; currentNode->prevNode = node; currentNode = node; + // NB: no need to update currentPosition cardinality++; } - else //( pos==ELP_next || pos==ELP_atpos ) - // insert after current node + else // (pos == ELP_next || pos == ELP_atpos) + // insert after current node { DcmListNode *node = new DcmListNode(obj); - if ( currentNode->nextNode == NULL ) - lastNode = node; // append to the end + if (currentNode->nextNode == NULL) + lastNode = node; // append to the end else currentNode->nextNode->prevNode = node; node->nextNode = currentNode->nextNode; node->prevNode = currentNode; currentNode->nextNode = node; currentNode = node; + currentPosition++; cardinality++; } + } else { + DCMDATA_DEBUG("DcmList::insert() cannot insert object, maximum number of entries reached"); + obj = NULL; } } // obj == NULL return obj; @@ -185,20 +221,20 @@ DcmObject *DcmList::remove() DcmObject *tempobj; DcmListNode *tempnode; - if ( DcmList::empty() ) // list is empty ! + if (DcmList::empty()) return NULL; - else if ( !DcmList::valid() ) + else if (!DcmList::valid()) return NULL; // current node is 0 else { tempnode = currentNode; - if ( currentNode->prevNode == NULL ) + if (currentNode->prevNode == NULL) firstNode = currentNode->nextNode; // delete first element else currentNode->prevNode->nextNode = currentNode->nextNode; - if ( currentNode->nextNode == NULL ) + if (currentNode->nextNode == NULL) lastNode = currentNode->prevNode; // delete last element else currentNode->nextNode->prevNode = currentNode->prevNode; @@ -206,6 +242,7 @@ DcmObject *DcmList::remove() currentNode = currentNode->nextNode; tempobj = tempnode->value(); delete tempnode; + // NB: no need to update currentPosition cardinality--; return tempobj; } @@ -215,32 +252,52 @@ DcmObject *DcmList::remove() // ******************************** -DcmObject *DcmList::get( E_ListPos pos ) +DcmObject *DcmList::get(const E_ListPos pos) { - return seek( pos ); + return seek(pos); } // ******************************** -DcmObject *DcmList::seek( E_ListPos pos ) +DcmObject *DcmList::seek(const E_ListPos pos) { switch (pos) { - case ELP_first : + case ELP_first: currentNode = firstNode; + if (DcmList::valid()) + currentPosition = 0; + else + currentPosition = invalidListPosition; break; - case ELP_last : + case ELP_last: currentNode = lastNode; + if (DcmList::valid()) + currentPosition = cardinality - 1; + else + currentPosition = invalidListPosition; break; - case ELP_prev : - if ( DcmList::valid() ) + case ELP_prev: + if (DcmList::valid()) + { currentNode = currentNode->prevNode; + if (DcmList::valid()) + currentPosition--; + else + currentPosition = invalidListPosition; + } break; - case ELP_next : - if ( DcmList::valid() ) + case ELP_next: + if (DcmList::valid()) + { currentNode = currentNode->nextNode; + if (DcmList::valid()) + currentPosition++; + else + currentPosition = invalidListPosition; + } break; default: break; @@ -252,26 +309,71 @@ DcmObject *DcmList::seek( E_ListPos pos ) // ******************************** -DcmObject *DcmList::seek_to(unsigned long absolute_position) +DcmObject *DcmList::seek_to(const unsigned long absolute_position) { + if (absolute_position >= cardinality) + { + // invalid position + currentNode = NULL; + currentPosition = invalidListPosition; + return NULL; + } + else if (absolute_position == 0) + { + // first item in the list + return seek(ELP_first); + } + else if (absolute_position == cardinality - 1) + { + // last item in the list + return seek(ELP_last); + } + else if (currentPosition != invalidListPosition) + { + // determine distance between current and requested position + const unsigned long distance = (absolute_position >= currentPosition) + ? (absolute_position - currentPosition) + : (currentPosition - absolute_position); + + // Are we seeking to a position that is closer to the current position than to + // the start or end of the sequence? This is often the case, if we are using + // seek_to() to essentially iterate over the sequence, for example. If so, then + // let's start iterating from the current position. Often, the position we want + // is simply the next position (or maybe the previous one). Let's make those + // use cases be O(1), and not O(n). + if ((distance <= absolute_position) && (distance < cardinality - absolute_position)) + { + if (currentPosition <= absolute_position) + { + while (currentPosition < absolute_position) + seek(ELP_next); + } + else + { + while (currentPosition > absolute_position) + seek(ELP_prev); + } + return get(ELP_atpos); + } + } + + // iterate from the start... if (absolute_position < cardinality / 2) { /* iterate over first half of the list */ - seek( ELP_first ); + seek(ELP_first); for (unsigned long i = 0; i < absolute_position; i++) - seek( ELP_next ); + seek(ELP_next); } - else if (absolute_position < cardinality) + else // ... or the end of the list { - /* iterate over second half of the list (starting from the end) */ - seek( ELP_last ); + assert(absolute_position < cardinality); + // iterate over second half of the list (starting from the end) + seek(ELP_last); for (unsigned long i = absolute_position + 1; i < cardinality; i++) - seek( ELP_prev ); - } else { - /* invalid position */ - currentNode = NULL; + seek(ELP_prev); } - return get( ELP_atpos ); + return get(ELP_atpos); } @@ -304,5 +406,6 @@ void DcmList::deleteAllElements() firstNode = NULL; lastNode = NULL; currentNode = NULL; + currentPosition = invalidListPosition; cardinality = 0; } diff --git a/dcmdata/libsrc/dcmetinf.cc b/dcmdata/libsrc/dcmetinf.cc index d3ce5927..8ff7ffbe 100644 --- a/dcmdata/libsrc/dcmetinf.cc +++ b/dcmdata/libsrc/dcmetinf.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -666,3 +666,10 @@ OFCondition DcmMetaInfo::loadFile(const OFFilename &fileName, } return l_error; } + +// ******************************** + +const char *DcmMetaInfo::getPreamble() const +{ + return filePreamble; +} diff --git a/dcmdata/libsrc/dcostrmf.cc b/dcmdata/libsrc/dcostrmf.cc index c3ea92c4..8aaea51c 100644 --- a/dcmdata/libsrc/dcostrmf.cc +++ b/dcmdata/libsrc/dcostrmf.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2024, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -26,9 +26,7 @@ #include "dcmtk/ofstd/ofconsol.h" BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include -#endif #ifdef HAVE_IO_H #include #endif diff --git a/dcmdata/libsrc/dcostrms.cc b/dcmdata/libsrc/dcostrms.cc index 4234c136..0b56d8c8 100644 --- a/dcmdata/libsrc/dcostrms.cc +++ b/dcmdata/libsrc/dcostrms.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2023, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -26,9 +26,7 @@ #include "dcmtk/ofstd/ofconsol.h" BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include -#endif #ifdef HAVE_IO_H #include #endif diff --git a/dcmdata/libsrc/dcpixel.cc b/dcmdata/libsrc/dcpixel.cc index 50451d60..eddd0596 100644 --- a/dcmdata/libsrc/dcpixel.cc +++ b/dcmdata/libsrc/dcpixel.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1997-2024, OFFIS e.V. + * Copyright (C) 1997-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -1298,27 +1298,77 @@ OFCondition DcmPixelData::writeJson(STD_NAMESPACE ostream &out, // check if we have an empty uncompressed value field. // We never encode that as BulkDataURI. OFBool emptyValue = OFFalse; - if ((current == repListEnd) && existUnencapsulated && (getLengthField() == 0)) + if ((current == repListEnd) && existUnencapsulated && (getLength() == 0)) { emptyValue = OFTrue; } - // now check if the pixel data will be written as - // BulkDataURI, which is possible for both uncompressed - // and encapsulated pixel data. - OFString value; - if ((! emptyValue) && format.asBulkDataURI(getTag(), value)) + // determine the length either of the pixel sequence or of the uncompressed pixel data + Uint32 len = 0; + if (current == repListEnd) len = getLength(); + else if ((*current)->pixSeq != NULL) len = (*current)->pixSeq->getLength(); + + // check if the pixel data should be written as BulkDataURI + if (((! emptyValue) || (current != repListEnd)) && format.asBulkDataURI(getTag(), len)) { - /* write JSON Opener */ - writeJsonOpener(out, format); + // We should write the pixel data as bulk data. Now check whether + // we are dealing with encapsulated or unencapsulated data + if ((current == repListEnd) && existUnencapsulated) + { + // Current pixel data representation is unencapsulated. + // Write JSON Opener + writeJsonOpener(out, format); - /* return defined BulkDataURI */ - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, value); + // adjust byte order to little endian + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); - /* write JSON Closer */ - writeJsonCloser(out, format); - return EC_Normal; + // write as bulk data + OFCondition status = format.writeBulkData(out, getTag(), getLengthField(), byteValues); + + // write JSON Closer + writeJsonCloser(out, format); + return status; + } + else if (current != repListEnd) + { + // Current pixel data representation is encapsulated. + // Access the parent item to check the number of frames + DcmItem *parentItem = getParentItem(); + if (parentItem == NULL) + { + // something is fishy with this dataset + DCMDATA_WARN("DcmPixelData: Unable to access parent dataset for pixel data object"); + return EC_CannotWriteBulkDataFile; + } + + // check NumberOfFrames attribute, default to 1 if absent or unreadable + Sint32 imageFrames = 1; + if (parentItem->findAndGetSint32(DCM_NumberOfFrames, imageFrames).bad()) imageFrames = 1; + if (imageFrames != 1) + { + DCMDATA_WARN("Encapsulated multi-frame images cannot be represented in JSON"); + return EC_CannotWriteJSONMultiframe; + } + + // (*current)->pixSeq is not NULL at this point, we have checked this earlier in this method + DcmPixelSequence *currentPixelSequence = (*current)->pixSeq; + + // write JSON Opener + writeJsonOpener(out, format); + + // delegate the JSON conversion to the pixel sequence + OFCondition status = currentPixelSequence->writeJson(out, format); + + // write JSON Closer + writeJsonCloser(out, format); + return status; + } + else + { + // something is fishy with this dataset + DCMDATA_WARN("DcmPixelData: apparently there is neither compressed nor uncompressed data"); + return EC_CannotWriteJsonInlineBinary; + } } // No bulk data URI, we're supposed to write as InlineBinary. @@ -1356,3 +1406,21 @@ OFCondition DcmPixelData::writeJson(STD_NAMESPACE ostream &out, // pixel data is encapsulated, return error return EC_CannotWriteJsonInlineBinary; } + + +Uint16 DcmPixelData::decodedBitsAllocated( + Uint16 bitsAllocated, + Uint16 bitsStored) const +{ + if (bitsStored > bitsAllocated) return 0; + if (existUnencapsulated || (original == repListEnd)) + { + // we have uncompressed pixel data or pixel data is empty + return DcmElement::decodedBitsAllocated(bitsAllocated, bitsStored); + } + else + { + return DcmCodecList::decodedBitsAllocated((*original)->repType, bitsAllocated, bitsStored); + } +} + diff --git a/dcmdata/libsrc/dcpixseq.cc b/dcmdata/libsrc/dcpixseq.cc index 7d81fb68..2cf95c13 100644 --- a/dcmdata/libsrc/dcpixseq.cc +++ b/dcmdata/libsrc/dcpixseq.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -24,14 +24,14 @@ #include "dcmtk/ofstd/ofstream.h" #include "dcmtk/ofstd/ofuuid.h" +#include "dcmtk/ofstd/ofsha256.h" #include "dcmtk/dcmdata/dcpixseq.h" #include "dcmtk/dcmdata/dcpxitem.h" #include "dcmtk/dcmdata/dcitem.h" #include "dcmtk/dcmdata/dcvr.h" - #include "dcmtk/dcmdata/dcdeftag.h" - +#include "dcmtk/dcmdata/dcjson.h" // ******************************** @@ -181,6 +181,100 @@ OFCondition DcmPixelSequence::writeXML(STD_NAMESPACE ostream &out, return l_error; } +// ******************************** + +OFCondition DcmPixelSequence::writeJson( + STD_NAMESPACE ostream &out, + DcmJsonFormat &format) +{ + // At this point, we can safely assume that this is a single frame image + // since this has been checked in DcmPixelData::writeJson(). + + OFCondition status = EC_Normal; + unsigned long numItems = card(); + if (numItems < 2) + { + DCMDATA_WARN("DcmPixelSequence: pixel sequence is empty"); + return EC_CannotWriteBulkDataFile; + } + + // compute SHA-2 checksum over all pixel data fragments, + // not including the basic offset table (i.e. the first item) + OFSHA256 sha256; + Uint8 hash[32]; + DcmPixelItem *pixItem = NULL; + Uint8 *pixelData = NULL; + Uint32 fragmentLength = 0; + for (unsigned long i = 1; i < numItems; ++i) + { + status = getItem(pixItem, i); + if (status.bad()) return status; + fragmentLength = pixItem->getLength(); + status = pixItem->getUint8Array(pixelData); + if (status.bad()) return status; + sha256.update(pixelData, fragmentLength); + } + sha256.final(hash); + + // determine filename and path + DcmXfer xfer(Xfer); + OFString bulkname; + char hashstring[3]; + for (int i=0; i < 32; ++i) + { + OFStandard::snprintf(hashstring, sizeof(hashstring), "%02x", hash[i]); + bulkname.append(hashstring); + } + bulkname.append(xfer.getFilenameExtension()); + + OFString bulkpath; + format.getBulkDataDirectory(bulkpath); + bulkpath.append(bulkname); + + /* check if file already exists. In this case, the file content is the same + * we would create now since the SHA-256 checksum is the same. So we can just + * use the existing file. + */ + if (! OFStandard::fileExists(bulkpath)) + { + OFFile bulkfile; + if (! bulkfile.fopen(bulkpath.c_str(), "wb")) + { + DCMDATA_ERROR("Unable to create bulk data file '" << bulkpath << "'"); + return EC_CannotWriteBulkDataFile; + } + + // write all fragments into a single file, as specified in DICOM part 18, section 8.7.3.3.2. + for (unsigned long i = 1; i < numItems; ++i) + { + status = getItem(pixItem, i); + if (status.bad()) return status; + fragmentLength = pixItem->getLength(); + status = pixItem->getUint8Array(pixelData); + if (status.bad()) return status; + if (fragmentLength != bulkfile.fwrite(pixelData, 1, fragmentLength)) + { + DCMDATA_ERROR("Unable to write bulk data to file '" << bulkpath << "'"); + return EC_CannotWriteBulkDataFile; + } + } + + if (bulkfile.fclose()) + { + DCMDATA_ERROR("Unable to close bulk data file '" << bulkpath << "'"); + return EC_CannotWriteBulkDataFile; + } + } + + // print BulkDataURI (the enclosing Json opener and closer are printed in class DcmPixelData) + format.printBulkDataURIPrefix(out); + OFString bulkDataURI; + format.getBulkDataURIPrefix(bulkDataURI); + bulkDataURI.append(bulkname); + DcmJsonFormat::printString(out, bulkDataURI); + return status; +} + // ******************************** diff --git a/dcmdata/libsrc/dcrleccd.cc b/dcmdata/libsrc/dcrleccd.cc index fd01b63b..6e284f78 100644 --- a/dcmdata/libsrc/dcrleccd.cc +++ b/dcmdata/libsrc/dcrleccd.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2024, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -60,6 +60,20 @@ OFBool DcmRLECodecDecoder::canChangeCoding( } +Uint16 DcmRLECodecDecoder::decodedBitsAllocated( + Uint16 bitsAllocated, + Uint16 /* bitsStored */) const +{ + // The RLE decoder only supports images where BitsAllocated is a multiple of 8. + if ((bitsAllocated < 8)||(bitsAllocated % 8 != 0)) return 0; + + // RLE cannot support more than 120 bits/sample (15 bands) in DICOM + if (bitsAllocated > 120) return 0; + + return bitsAllocated; +} + + OFCondition DcmRLECodecDecoder::decode( const DcmRepresentationParameter * /* fromRepParam */, DcmPixelSequence * pixSeq, @@ -298,7 +312,7 @@ OFCondition DcmRLECodecDecoder::decode( { if (rledecoder.size() < bytesPerStripe) { - DCMDATA_WARN("RLE decoder is finished but has produced insufficient data for this stripe, will continue with next pixel item"); + DCMDATA_WARN("DcmRLECodecDecoder: Detected fragmented RLE compressed pixel data, which is not allowed in DICOM"); DCMDATA_DEBUG("RLE decoder processes pixel item " << currentItem); result = pixSeq->getItem(pixItem, currentItem++); if (result.good()) @@ -334,6 +348,7 @@ OFCondition DcmRLECodecDecoder::decode( if (result.good() || result == EC_StreamNotifyClient) { + DCMDATA_WARN("DcmRLECodecDecoder: Detected fragmented RLE compressed pixel data, which is not allowed in DICOM"); DCMDATA_DEBUG("RLE decoder processes pixel item " << currentItem); result = pixSeq->getItem(pixItem, currentItem++); } @@ -348,6 +363,12 @@ OFCondition DcmRLECodecDecoder::decode( } /* while */ // last fragment for this RLE stripe + if (inputBytes + byteOffset > fragmentLength) + { + DCMDATA_WARN("DcmRLECodecDecoder: Stream size in RLE header is wrong, adjusting from " << inputBytes << " to " << fragmentLength-byteOffset << " bytes"); + inputBytes = fragmentLength-byteOffset; + } + result = rledecoder.decompress(rleData + byteOffset, OFstatic_cast(size_t, inputBytes)); // special handling for zero pad byte at the end of the RLE stream @@ -366,7 +387,7 @@ OFCondition DcmRLECodecDecoder::decode( if (lastStripeOfColor && (rledecoder.size() < bytesPerStripe)) { // stripe ended prematurely? report a warning and continue - DCMDATA_WARN("RLE decoder is finished but has produced insufficient data for this stripe, filling remaining pixels"); + DCMDATA_WARN("DcmRLECodecDecoder: RLE decoder is finished but has produced insufficient data for this stripe, filling remaining pixels"); result = EC_Normal; } else if (rledecoder.size() != bytesPerStripe) @@ -617,6 +638,11 @@ OFCondition DcmRLECodecDecoder::decodeFrame( // if the RLE data is split in multiple fragments. We need to feed // data fragment by fragment until the RLE codec has produced // sufficient output. + if (fragmentLength < byteOffset) + { + DCMDATA_ERROR("Byte offset in RLE header is wrong."); + return EC_CannotChangeRepresentation; + } bytesToDecode = OFstatic_cast(size_t, fragmentLength - byteOffset); } else @@ -724,6 +750,10 @@ OFCondition DcmRLECodecDecoder::decodeFrame( } // adjust byte order for uncompressed image to little endian + if ((gLocalByteOrder == EBO_BigEndian) && (frameSize & 1)) + { + DCMDATA_WARN("Size of frame buffer is odd, cannot correct byte order for last pixel value"); + } swapIfNecessary(EBO_LittleEndian, gLocalByteOrder, imageData16, frameSize, sizeof(Uint16)); return result; diff --git a/dcmdata/libsrc/dcrlecce.cc b/dcmdata/libsrc/dcrlecce.cc index ef835f4e..f8ba1f73 100644 --- a/dcmdata/libsrc/dcrlecce.cc +++ b/dcmdata/libsrc/dcrlecce.cc @@ -62,6 +62,14 @@ OFBool DcmRLECodecEncoder::canChangeCoding( } +Uint16 DcmRLECodecEncoder::decodedBitsAllocated( + Uint16 /* bitsAllocated */, + Uint16 /* bitsStored */) const +{ + return 0; +} + + OFCondition DcmRLECodecEncoder::decode( const DcmRepresentationParameter * /* fromRepParam */, DcmPixelSequence * /* pixSeq */, diff --git a/dcmdata/libsrc/dcspchrs.cc b/dcmdata/libsrc/dcspchrs.cc index c4f0ec29..9f548166 100644 --- a/dcmdata/libsrc/dcspchrs.cc +++ b/dcmdata/libsrc/dcspchrs.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2011-2024, OFFIS e.V. + * Copyright (C) 2011-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -175,12 +175,12 @@ OFCondition DcmSpecificCharacterSet::selectCharacterSet(const OFString &fromChar if (sourceVM == 0) { // no character set specified, use ASCII - status = DefaultEncodingConverter.selectEncoding("ASCII", DestinationEncoding); + status = DefaultEncodingConverter.selectEncoding("ASCII", getDestinationEncoding()); // output some useful debug information if (status.good()) { DCMDATA_DEBUG("DcmSpecificCharacterSet: Selected character set '' (ASCII) " - << "for the conversion to " << DestinationEncoding); + << "for the conversion to " << getDestinationEncoding()); } } else if (sourceVM == 1) @@ -327,12 +327,12 @@ OFCondition DcmSpecificCharacterSet::selectCharacterSetWithoutCodeExtensions() // check whether an appropriate character encoding has been found if (!fromEncoding.empty()) { - status = DefaultEncodingConverter.selectEncoding(fromEncoding, DestinationEncoding); + status = DefaultEncodingConverter.selectEncoding(fromEncoding, getDestinationEncoding()); // output some useful debug information if (status.good()) { DCMDATA_DEBUG("DcmSpecificCharacterSet: Selected character set '" << SourceCharacterSet - << "' (" << fromEncoding << ") for the conversion to " << DestinationEncoding); + << "' (" << fromEncoding << ") for the conversion to " << getDestinationEncoding()); } } return status; @@ -469,12 +469,12 @@ OFCondition DcmSpecificCharacterSet::selectCharacterSetWithCodeExtensions(const // but first check whether this encoding has already been added before if (conv.second) { - status = conv.first->second.selectEncoding(encodingName, DestinationEncoding); + status = conv.first->second.selectEncoding(encodingName, getDestinationEncoding()); if (status.good()) { // output some useful debug information DCMDATA_DEBUG("DcmSpecificCharacterSet: Added character set '" << definedTerm - << "' (" << encodingName << ") for the conversion to " << DestinationEncoding); + << "' (" << encodingName << ") for the conversion to " << getDestinationEncoding()); // also remember the default descriptor, which refers to the first character set if (i == 0) { @@ -502,12 +502,12 @@ OFCondition DcmSpecificCharacterSet::selectCharacterSetWithCodeExtensions(const OFMake_pair(OFString("ISO 2022 IR 6"), OFCharacterEncoding())); if (conv.second) { - status = conv.first->second.selectEncoding("ASCII", DestinationEncoding); + status = conv.first->second.selectEncoding("ASCII", getDestinationEncoding()); if (status.good()) { // output some useful debug information DCMDATA_DEBUG("DcmSpecificCharacterSet: Added character set 'ISO 2022 IR 6' (ASCII) " - << "for the conversion to " << DestinationEncoding + << "for the conversion to " << getDestinationEncoding() << " (because it is needed for one or more of the previously added character sets)"); } else { DCMDATA_ERROR("DcmSpecificCharacterSet: 'ISO 2022 IR 6' is not supported by" @@ -540,108 +540,61 @@ OFCondition DcmSpecificCharacterSet::convertString(const char *fromString, const OFBool hasEscapeChar = checkForEscapeCharacter(fromString, fromLength); if (EncodingConverters.empty() || (!hasEscapeChar && delimiters.empty())) { - if (delimiters.empty()) + // convert string without code extensions according to ISO 2022 + status = convertStringWithoutCodeExtensions(fromString, fromLength, toString, delimiters); + } else { + // convert string with code extensions according to ISO 2022 + status = convertStringWithCodeExtensions(fromString, fromLength, toString, delimiters, hasEscapeChar); + } + if (status.good()) + { + // finally, output some debug information + if (getDestinationEncoding() == "UTF-8") { - // no code extensions according to ISO 2022 used and no delimiters - this is the simple case - DCMDATA_DEBUG("DcmSpecificCharacterSet: Converting '" - << convertToLengthLimitedOctalString(fromString, fromLength) << "'"); - status = DefaultEncodingConverter.convertString(fromString, fromLength, toString, OFTrue /*clearMode*/); + // output code points only in case of UTF-8 output + DCMDATA_TRACE("Converted result in " << getDestinationEncoding() << " is '" + << convertToLengthLimitedOctalString(toString.c_str(), toString.length()) << "' (" + << countCharactersInUTF8String(toString) << " code points)"); } else { - // no code extensions according to ISO 2022 used, but delimiters - DCMDATA_DEBUG("DcmSpecificCharacterSet: Converting '" - << convertToLengthLimitedOctalString(fromString, fromLength) - << "' (with delimiters '" << delimiters << "')"); - - toString.clear(); - size_t pos = 0; - const char *firstChar = fromString; - const char *currentChar = fromString; - - // iterate over all characters of the string (as long as there is no error) - while ((pos < fromLength) && status.good()) - { - const char c0 = *currentChar++; + DCMDATA_TRACE("Converted result in " << getDestinationEncoding() << " is '" + << convertToLengthLimitedOctalString(toString.c_str(), toString.length()) << "'"); + } + } + return status; +} - // check for characters ESC, HT, LF, FF, CR or any other specified delimiter - if ((c0 == '\011') || (c0 == '\012') || (c0 == '\014') || (c0 == '\015') || (delimiters.find(c0) != OFString_npos)) - { - // convert the sub-string (before the delimiter) with the current character set - const size_t convertLength = currentChar - firstChar - 1; - if (convertLength > 0) - { - // output some debug information - DCMDATA_TRACE(" Converting sub-string '" - << convertToLengthLimitedOctalString(firstChar, convertLength) << "'"); - status = DefaultEncodingConverter.convertString(firstChar, convertLength, toString, OFFalse /*clearMode*/); - if (status.bad()) - DCMDATA_TRACE(" -> ERROR: " << status.text()); - } - // output some debug information - DCMDATA_TRACE(" Appending delimiter '" - << convertToLengthLimitedOctalString(currentChar - 1 /* identical to c0 */, 1) - << "' to the output"); - // don't forget to append the delimiter - toString += c0; - - // start new sub-string after delimiter - firstChar = currentChar; - } - ++pos; - } - if (status.good()) - { - // convert any remaining characters from the input string - const size_t convertLength = currentChar - firstChar; - if (convertLength > 0) - { - // output some debug information - DCMDATA_TRACE(" Converting remaining sub-string '" - << convertToLengthLimitedOctalString(firstChar, convertLength) << "'"); - status = DefaultEncodingConverter.convertString(firstChar, convertLength, toString, OFFalse /*clearMode*/); - if (status.bad()) - DCMDATA_TRACE(" -> ERROR: " << status.text()); - } - } - } +OFCondition DcmSpecificCharacterSet::convertStringWithoutCodeExtensions(const char *fromString, + const size_t fromLength, + OFString &toString, + const OFString &delimiters) +{ + OFCondition status = EC_Normal; + // any delimiters defined? + if (delimiters.empty()) + { + // case 1: no code extensions used and no delimiters defined - this is the simple case + DCMDATA_DEBUG("DcmSpecificCharacterSet: Converting '" + << convertToLengthLimitedOctalString(fromString, fromLength) << "'"); + status = DefaultEncodingConverter.convertString(fromString, fromLength, toString, OFTrue /*clearMode*/); } else { - if (delimiters.empty()) - { - DCMDATA_DEBUG("DcmSpecificCharacterSet: Converting '" - << convertToLengthLimitedOctalString(fromString, fromLength) - << "' (with code extensions)"); - } else { - DCMDATA_DEBUG("DcmSpecificCharacterSet: Converting '" - << convertToLengthLimitedOctalString(fromString, fromLength) - << "' (with " << (hasEscapeChar ? "code extensions and " : "") - << "delimiters '" << delimiters << "')"); - } - // code extensions according to ISO 2022 (possibly) used, so we need to check - // for particular escape sequences in order to switch between character sets + // case 2: no code extensions used, but delimiters defined + DCMDATA_DEBUG("DcmSpecificCharacterSet: Converting '" + << convertToLengthLimitedOctalString(fromString, fromLength) + << "' (with delimiters '" << delimiters << "')"); + toString.clear(); size_t pos = 0; - // some (extended) character sets use more than 1 byte per character - // (however, the default character set always uses a single byte) - unsigned char bytesPerChar = 1; - // check whether '=' is a delimiter, as it is used in PN values - OFBool isFirstGroup = (delimiters.find('=') != OFString_npos); - // by default, we expect that delimiters can be checked by their corresponding ASCII codes - // (this implies that the default character set is not "ISO 2022 IR 87" or "ISO 2022 IR 159") - OFBool checkDelimiters = OFTrue; const char *firstChar = fromString; const char *currentChar = fromString; - // initially, use the default descriptor - OFCharacterEncoding converter = DefaultEncodingConverter; - DCMDATA_TRACE(" Starting with the default character set"); // iterate over all characters of the string (as long as there is no error) while ((pos < fromLength) && status.good()) { const char c0 = *currentChar++; - // check for characters ESC, HT, LF, FF, CR or any other specified delimiter - const OFBool isEscape = (c0 == '\033'); - const OFBool isDelimiter = checkDelimiters && - ((c0 == '\011') || (c0 == '\012') || (c0 == '\014') || (c0 == '\015') || (delimiters.find(c0) != OFString_npos)); - if (isEscape || isDelimiter) + // check for characters HT, LF, FF, CR or any other specified delimiter + const OFBool isDelimiter = ((c0 == '\011') || (c0 == '\012') || (c0 == '\014') || (c0 == '\015') || + (delimiters.find(c0) != OFString_npos)); + if (isDelimiter) { // convert the sub-string (before the delimiter) with the current character set const size_t convertLength = currentChar - firstChar - 1; @@ -650,178 +603,19 @@ OFCondition DcmSpecificCharacterSet::convertString(const char *fromString, // output some debug information DCMDATA_TRACE(" Converting sub-string '" << convertToLengthLimitedOctalString(firstChar, convertLength) << "'"); - status = converter.convertString(firstChar, convertLength, toString, OFFalse /*clearMode*/); + status = DefaultEncodingConverter.convertString(firstChar, convertLength, toString, OFFalse /*clearMode*/); if (status.bad()) DCMDATA_TRACE(" -> ERROR: " << status.text()); } - // check whether this was the first component group of a PN value - if (isDelimiter && (c0 == '=')) - isFirstGroup = OFFalse; - } - // the ESC character is used to explicitly switch between character sets - if (isEscape) - { - // report a warning as this is a violation of DICOM PS 3.5 Section 6.2.1 - if (isFirstGroup) - { - DCMDATA_WARN("DcmSpecificCharacterSet: Escape sequences shall not be used " - << "in the first component group of a Person Name (PN), using them anyway"); - } - // we need at least two more characters to determine the new character set - size_t escLength = 2; - if (pos + escLength < fromLength) - { - OFString key; - const char c1 = *currentChar++; - const char c2 = *currentChar++; - char c3 = '\0'; - if ((c1 == 0x28) && (c2 == 0x42)) // ASCII - key = "ISO 2022 IR 6"; - else if ((c1 == 0x2d) && (c2 == 0x41)) // Latin alphabet No. 1 - key = "ISO 2022 IR 100"; - else if ((c1 == 0x2d) && (c2 == 0x42)) // Latin alphabet No. 2 - key = "ISO 2022 IR 101"; - else if ((c1 == 0x2d) && (c2 == 0x43)) // Latin alphabet No. 3 - key = "ISO 2022 IR 109"; - else if ((c1 == 0x2d) && (c2 == 0x44)) // Latin alphabet No. 4 - key = "ISO 2022 IR 110"; - else if ((c1 == 0x2d) && (c2 == 0x4c)) // Cyrillic - key = "ISO 2022 IR 144"; - else if ((c1 == 0x2d) && (c2 == 0x47)) // Arabic - key = "ISO 2022 IR 127"; - else if ((c1 == 0x2d) && (c2 == 0x46)) // Greek - key = "ISO 2022 IR 126"; - else if ((c1 == 0x2d) && (c2 == 0x48)) // Hebrew - key = "ISO 2022 IR 138"; - else if ((c1 == 0x2d) && (c2 == 0x4d)) // Latin alphabet No. 5 - key = "ISO 2022 IR 148"; - else if ((c1 == 0x2d) && (c2 == 0x62)) // Latin alphabet No. 9 - key = "ISO 2022 IR 203"; - else if ((c1 == 0x29) && (c2 == 0x49)) // Japanese, JIS X0201, G1 set (Katakana) - key = "ISO 2022 IR 13"; - else if ((c1 == 0x28) && (c2 == 0x4a)) // Japanese, JIS X0201, G0 set (Romaji, i.e. ASCII) - key = "ISO 2022 IR 13"; - else if ((c1 == 0x2d) && (c2 == 0x54)) // Thai - key = "ISO 2022 IR 166"; - else if ((c1 == 0x24) && (c2 == 0x42)) // Japanese (multi-byte), JIS X0208 (Kanji) - key = "ISO 2022 IR 87"; - else if ((c1 == 0x24) && (c2 == 0x28)) // Japanese (multi-byte), JIS X0212 (Supplementary Kanji set) - { - escLength = 3; - // do we still have another character in the string? - if (pos + escLength < fromLength) - { - c3 = *currentChar++; - if (c3 == 0x44) - key = "ISO 2022 IR 159"; - } - } - else if ((c1 == 0x24) && (c2 == 0x29)) // might be Korean or Chinese - { - escLength = 3; - // do we still have another character in the string? - if (pos + escLength < fromLength) - { - c3 = *currentChar++; - if (c3 == 0x43) // Korean (single- and multi-byte) - key = "ISO 2022 IR 149"; - else if (c3 == 0x41) // Simplified Chinese (multi-byte) - key = "ISO 2022 IR 58"; - } - } - // check whether a valid escape sequence has been found - if (key.empty()) - { - OFOStringStream stream; - stream << "Cannot convert character set: Illegal escape sequence 'ESC " - << STD_NAMESPACE dec << STD_NAMESPACE setfill('0') - << STD_NAMESPACE setw(2) << OFstatic_cast(int, c1 >> 4) << "/" - << STD_NAMESPACE setw(2) << OFstatic_cast(int, c1 & 0x0f) << " " - << STD_NAMESPACE setw(2) << OFstatic_cast(int, c2 >> 4) << "/" - << STD_NAMESPACE setw(2) << OFstatic_cast(int, c2 & 0x0f); - if (escLength == 3) - { - stream << " " << STD_NAMESPACE setw(2) << OFstatic_cast(int, c3 >> 4) << "/" - << STD_NAMESPACE setw(2) << OFstatic_cast(int, c3 & 0x0f); - } - stream << "' found" << OFStringStream_ends; - OFSTRINGSTREAM_GETOFSTRING(stream, message) - status = makeOFCondition(OFM_dcmdata, EC_CODE_CannotConvertCharacterSet, OF_error, message.c_str()); - } - if (status.good()) - { - DCMDATA_TRACE(" Switching to character set '" << key << "'"); - T_EncodingConvertersMap::const_iterator it = EncodingConverters.find(key); - // check whether the descriptor was found in the map, i.e. properly declared in (0008,0005) - if (it != EncodingConverters.end()) - { - converter = it->second; - // special case: these Japanese character sets replace the ASCII part (G0 code area), - // so according to DICOM PS 3.5 Section 6.2.1.2 an explicit switch to the default is required - checkDelimiters = (key != "ISO 2022 IR 87") && (key != "ISO 2022 IR 159"); - // determine number of bytes per character (used by the selected character set) - if ((key == "ISO 2022 IR 87") || (key == "ISO 2022 IR 159") || (key == "ISO 2022 IR 58")) - { - DCMDATA_TRACE(" Now using 2 bytes per character"); - bytesPerChar = 2; - } - else if (key == "ISO 2022 IR 149") - { - DCMDATA_TRACE(" Now using 1 or 2 bytes per character"); - bytesPerChar = 0; // special handling for single- and multi-byte - } else { - DCMDATA_TRACE(" Now using 1 byte per character"); - bytesPerChar = 1; - } - } else { - OFOStringStream stream; - stream << "Cannot convert character set: Escape sequence refers to character set '" << key << "' that " - "was not declared in SpecificCharacterSet (0008,0005)" << OFStringStream_ends; - OFSTRINGSTREAM_GETOFSTRING(stream, message) - status = makeOFCondition(OFM_dcmdata, EC_CODE_CannotConvertCharacterSet, OF_error, message.c_str()); - } - } - pos += escLength; - } - // check whether the escape sequence was complete - if (status.good() && (pos >= fromLength)) - { - OFOStringStream stream; - stream << "Cannot convert character set: Incomplete escape sequence (" << (escLength + 1) - << " bytes expected) at the end of the string to be converted" << OFStringStream_ends; - OFSTRINGSTREAM_GETOFSTRING(stream, message) - status = makeOFCondition(OFM_dcmdata, EC_CODE_CannotConvertCharacterSet, OF_error, message.c_str()); - } - // do not copy the escape sequence to the output - firstChar = currentChar; - } - // the HT, LF, FF, CR character or other delimiters (depending on the VR) also cause a switch - else if (isDelimiter) - { // output some debug information DCMDATA_TRACE(" Appending delimiter '" << convertToLengthLimitedOctalString(currentChar - 1 /* identical to c0 */, 1) << "' to the output"); // don't forget to append the delimiter toString += c0; - // use the default descriptor again (see DICOM PS 3.5) - if (converter != DefaultEncodingConverter) - { - DCMDATA_TRACE(" Switching back to the default character set (because a delimiter was found)"); - converter = DefaultEncodingConverter; - checkDelimiters = OFTrue; - } // start new sub-string after delimiter firstChar = currentChar; } - // skip remaining bytes of current character (if any) - else if (bytesPerChar != 1) - { - const size_t skipBytes = (bytesPerChar > 0) ? (bytesPerChar - 1) : ((c0 & 0x80) ? 1 : 0); - if (pos + skipBytes < fromLength) - currentChar += skipBytes; - pos += skipBytes; - } ++pos; } if (status.good()) @@ -833,42 +627,263 @@ OFCondition DcmSpecificCharacterSet::convertString(const char *fromString, // output some debug information DCMDATA_TRACE(" Converting remaining sub-string '" << convertToLengthLimitedOctalString(firstChar, convertLength) << "'"); + status = DefaultEncodingConverter.convertString(firstChar, convertLength, toString, OFFalse /*clearMode*/); + if (status.bad()) + DCMDATA_TRACE(" -> ERROR: " << status.text()); + } + } + } + return status; +} + + +OFCondition DcmSpecificCharacterSet::convertStringWithCodeExtensions(const char *fromString, + const size_t fromLength, + OFString &toString, + const OFString &delimiters, + const OFBool hasEscapeChar) +{ + OFCondition status = EC_Normal; + // any delimiters defined? + if (delimiters.empty()) + { + // case 3: code extensions used, but no delimiters defined + DCMDATA_DEBUG("DcmSpecificCharacterSet: Converting '" + << convertToLengthLimitedOctalString(fromString, fromLength) + << "' (with code extensions)"); + } else { + // case 4: code extensions used and delimiters defined + DCMDATA_DEBUG("DcmSpecificCharacterSet: Converting '" + << convertToLengthLimitedOctalString(fromString, fromLength) + << "' (with " << (hasEscapeChar ? "code extensions and " : "") + << "delimiters '" << delimiters << "')"); + } + // code extensions according to ISO 2022 (possibly) used, so we need to check + // for particular escape sequences in order to switch between character sets + toString.clear(); + size_t pos = 0; + // some (extended) character sets use more than 1 byte per character + // (however, the default character set always uses a single byte) + unsigned char bytesPerChar = 1; + // check whether '=' is a delimiter, as it is used in PN values + OFBool isFirstGroup = (delimiters.find('=') != OFString_npos); + // by default, we expect that delimiters can be checked by their corresponding ASCII codes + // (this implies that the default character set is not "ISO 2022 IR 87" or "ISO 2022 IR 159") + OFBool checkDelimiters = OFTrue; + const char *firstChar = fromString; + const char *currentChar = fromString; + // initially, use the default descriptor + OFCharacterEncoding converter = DefaultEncodingConverter; + DCMDATA_TRACE(" Starting with the default character set"); + // iterate over all characters of the string (as long as there is no error) + while ((pos < fromLength) && status.good()) + { + const char c0 = *currentChar++; + // check for characters ESC, HT, LF, FF, CR or any other specified delimiter + const OFBool isEscape = (c0 == '\033'); + const OFBool isDelimiter = checkDelimiters && + ((c0 == '\011') || (c0 == '\012') || (c0 == '\014') || (c0 == '\015') || (delimiters.find(c0) != OFString_npos)); + if (isEscape || isDelimiter) + { + // convert the sub-string (before the delimiter) with the current character set + const size_t convertLength = currentChar - firstChar - 1; + if (convertLength > 0) + { + // output some debug information + DCMDATA_TRACE(" Converting sub-string '" + << convertToLengthLimitedOctalString(firstChar, convertLength) << "'"); status = converter.convertString(firstChar, convertLength, toString, OFFalse /*clearMode*/); if (status.bad()) DCMDATA_TRACE(" -> ERROR: " << status.text()); } + // check whether this was the first component group of a PN value + if (isDelimiter && (c0 == '=')) + isFirstGroup = OFFalse; } + // the ESC character is used to explicitly switch between character sets + if (isEscape) + { + // report a warning as this is a violation of DICOM PS 3.5 Section 6.2.1 + if (isFirstGroup) + { + DCMDATA_WARN("DcmSpecificCharacterSet: Escape sequences shall not be used " + << "in the first component group of a Person Name (PN), using them anyway"); + } + // we need at least two more characters to determine the new character set + size_t escLength = 2; + if (pos + escLength < fromLength) + { + OFString key; + const char c1 = *currentChar++; + const char c2 = *currentChar++; + char c3 = '\0'; + if ((c1 == 0x28) && (c2 == 0x42)) // ASCII + key = "ISO 2022 IR 6"; + else if ((c1 == 0x2d) && (c2 == 0x41)) // Latin alphabet No. 1 + key = "ISO 2022 IR 100"; + else if ((c1 == 0x2d) && (c2 == 0x42)) // Latin alphabet No. 2 + key = "ISO 2022 IR 101"; + else if ((c1 == 0x2d) && (c2 == 0x43)) // Latin alphabet No. 3 + key = "ISO 2022 IR 109"; + else if ((c1 == 0x2d) && (c2 == 0x44)) // Latin alphabet No. 4 + key = "ISO 2022 IR 110"; + else if ((c1 == 0x2d) && (c2 == 0x4c)) // Cyrillic + key = "ISO 2022 IR 144"; + else if ((c1 == 0x2d) && (c2 == 0x47)) // Arabic + key = "ISO 2022 IR 127"; + else if ((c1 == 0x2d) && (c2 == 0x46)) // Greek + key = "ISO 2022 IR 126"; + else if ((c1 == 0x2d) && (c2 == 0x48)) // Hebrew + key = "ISO 2022 IR 138"; + else if ((c1 == 0x2d) && (c2 == 0x4d)) // Latin alphabet No. 5 + key = "ISO 2022 IR 148"; + else if ((c1 == 0x2d) && (c2 == 0x62)) // Latin alphabet No. 9 + key = "ISO 2022 IR 203"; + else if ((c1 == 0x29) && (c2 == 0x49)) // Japanese, JIS X0201, G1 set (Katakana) + key = "ISO 2022 IR 13"; + else if ((c1 == 0x28) && (c2 == 0x4a)) // Japanese, JIS X0201, G0 set (Romaji, i.e. ASCII) + key = "ISO 2022 IR 13"; + else if ((c1 == 0x2d) && (c2 == 0x54)) // Thai + key = "ISO 2022 IR 166"; + else if ((c1 == 0x24) && (c2 == 0x42)) // Japanese (multi-byte), JIS X0208 (Kanji) + key = "ISO 2022 IR 87"; + else if ((c1 == 0x24) && (c2 == 0x28)) // Japanese (multi-byte), JIS X0212 (Supplementary Kanji set) + { + escLength = 3; + // do we still have another character in the string? + if (pos + escLength < fromLength) + { + c3 = *currentChar++; + if (c3 == 0x44) + key = "ISO 2022 IR 159"; + } + } + else if ((c1 == 0x24) && (c2 == 0x29)) // might be Korean or Chinese + { + escLength = 3; + // do we still have another character in the string? + if (pos + escLength < fromLength) + { + c3 = *currentChar++; + if (c3 == 0x43) // Korean (single- and multi-byte) + key = "ISO 2022 IR 149"; + else if (c3 == 0x41) // Simplified Chinese (multi-byte) + key = "ISO 2022 IR 58"; + } + } + // check whether a valid escape sequence has been found + if (key.empty()) + { + OFOStringStream stream; + stream << "Cannot convert character set: Illegal escape sequence 'ESC " + << STD_NAMESPACE dec << STD_NAMESPACE setfill('0') + << STD_NAMESPACE setw(2) << OFstatic_cast(int, c1 >> 4) << "/" + << STD_NAMESPACE setw(2) << OFstatic_cast(int, c1 & 0x0f) << " " + << STD_NAMESPACE setw(2) << OFstatic_cast(int, c2 >> 4) << "/" + << STD_NAMESPACE setw(2) << OFstatic_cast(int, c2 & 0x0f); + if (escLength == 3) + { + stream << " " << STD_NAMESPACE setw(2) << OFstatic_cast(int, c3 >> 4) << "/" + << STD_NAMESPACE setw(2) << OFstatic_cast(int, c3 & 0x0f); + } + stream << "' found" << OFStringStream_ends; + OFSTRINGSTREAM_GETOFSTRING(stream, message) + status = makeOFCondition(OFM_dcmdata, EC_CODE_CannotConvertCharacterSet, OF_error, message.c_str()); + } + if (status.good()) + { + DCMDATA_TRACE(" Switching to character set '" << key << "'"); + T_EncodingConvertersMap::const_iterator it = EncodingConverters.find(key); + // check whether the descriptor was found in the map, i.e. properly declared in (0008,0005) + if (it != EncodingConverters.end()) + { + converter = it->second; + // special case: these Japanese character sets replace the ASCII part (G0 code area), + // so according to DICOM PS 3.5 Section 6.2.1.2 an explicit switch to the default is required + checkDelimiters = (key != "ISO 2022 IR 87") && (key != "ISO 2022 IR 159"); + // determine number of bytes per character (used by the selected character set) + if ((key == "ISO 2022 IR 87") || (key == "ISO 2022 IR 159") || (key == "ISO 2022 IR 58")) + { + DCMDATA_TRACE(" Now using 2 bytes per character"); + bytesPerChar = 2; + } + else if (key == "ISO 2022 IR 149") + { + DCMDATA_TRACE(" Now using 1 or 2 bytes per character"); + bytesPerChar = 0; // special handling for single- and multi-byte + } else { + DCMDATA_TRACE(" Now using 1 byte per character"); + bytesPerChar = 1; + } + } else { + OFOStringStream stream; + stream << "Cannot convert character set: Escape sequence refers to character set '" << key << "' that " + "was not declared in SpecificCharacterSet (0008,0005)" << OFStringStream_ends; + OFSTRINGSTREAM_GETOFSTRING(stream, message) + status = makeOFCondition(OFM_dcmdata, EC_CODE_CannotConvertCharacterSet, OF_error, message.c_str()); + } + } + pos += escLength; + } + // check whether the escape sequence was complete + if (status.good() && (pos >= fromLength)) + { + OFOStringStream stream; + stream << "Cannot convert character set: Incomplete escape sequence (" << (escLength + 1) + << " bytes expected) at the end of the string to be converted" << OFStringStream_ends; + OFSTRINGSTREAM_GETOFSTRING(stream, message) + status = makeOFCondition(OFM_dcmdata, EC_CODE_CannotConvertCharacterSet, OF_error, message.c_str()); + } + // do not copy the escape sequence to the output + firstChar = currentChar; + } + // the HT, LF, FF, CR character or other delimiters (depending on the VR) also cause a switch + else if (isDelimiter) + { + // output some debug information + DCMDATA_TRACE(" Appending delimiter '" + << convertToLengthLimitedOctalString(currentChar - 1 /* identical to c0 */, 1) + << "' to the output"); + // don't forget to append the delimiter + toString += c0; + // use the default descriptor again (see DICOM PS 3.5) + if (converter != DefaultEncodingConverter) + { + DCMDATA_TRACE(" Switching back to the default character set (because a delimiter was found)"); + converter = DefaultEncodingConverter; + checkDelimiters = OFTrue; + } + // start new sub-string after delimiter + firstChar = currentChar; + } + // skip remaining bytes of current character (if any) + else if (bytesPerChar != 1) + { + const size_t skipBytes = (bytesPerChar > 0) ? (bytesPerChar - 1) : ((c0 & 0x80) ? 1 : 0); + if (pos + skipBytes < fromLength) + currentChar += skipBytes; + pos += skipBytes; + } + ++pos; } if (status.good()) { - // finally, output some debug information - if (DestinationEncoding == "UTF-8") + // convert any remaining characters from the input string + const size_t convertLength = currentChar - firstChar; + if (convertLength > 0) { - // output code points only in case of UTF-8 output - DCMDATA_TRACE("Converted result in " << DestinationEncoding << " is '" - << convertToLengthLimitedOctalString(toString.c_str(), toString.length()) << "' (" - << countCharactersInUTF8String(toString) << " code points)"); - } else { - DCMDATA_TRACE("Converted result in " << DestinationEncoding << " is '" - << convertToLengthLimitedOctalString(toString.c_str(), toString.length()) << "'"); + // output some debug information + DCMDATA_TRACE(" Converting remaining sub-string '" + << convertToLengthLimitedOctalString(firstChar, convertLength) << "'"); + status = converter.convertString(firstChar, convertLength, toString, OFFalse /*clearMode*/); + if (status.bad()) + DCMDATA_TRACE(" -> ERROR: " << status.text()); } } return status; } -OFBool DcmSpecificCharacterSet::isConversionAvailable() -{ - // just call the appropriate function from the underlying class - return OFCharacterEncoding::isLibraryAvailable(); -} - - -size_t DcmSpecificCharacterSet::countCharactersInUTF8String(const OFString &utf8String) -{ - // just call the appropriate function from the underlying class - return OFCharacterEncoding::countCharactersInUTF8String(utf8String); -} OFBool DcmSpecificCharacterSet::checkForEscapeCharacter(const char *strValue, @@ -905,3 +920,19 @@ OFString DcmSpecificCharacterSet::convertToLengthLimitedOctalString(const char * // return string by-value (in order to avoid another parameter) return octalString; } + + +// static helper functions + +OFBool DcmSpecificCharacterSet::isConversionAvailable() +{ + // just call the appropriate function from the underlying class + return OFCharacterEncoding::isLibraryAvailable(); +} + + +size_t DcmSpecificCharacterSet::countCharactersInUTF8String(const OFString &utf8String) +{ + // just call the appropriate function from the underlying class + return OFCharacterEncoding::countCharactersInUTF8String(utf8String); +} diff --git a/dcmdata/libsrc/dcuid.cc b/dcmdata/libsrc/dcuid.cc index 93eeeba7..be463a81 100644 --- a/dcmdata/libsrc/dcuid.cc +++ b/dcmdata/libsrc/dcuid.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -35,9 +35,7 @@ BEGIN_EXTERN_C #ifdef HAVE_SYS_TIME_H #include #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_SOCKET_H #ifndef DCOMPAT_SYS_SOCKET_H_ #define DCOMPAT_SYS_SOCKET_H_ @@ -126,6 +124,7 @@ static const UIDNameMap uidNameMap[] = { { UID_JPEGLSLossyTransferSyntax, "JPEGLSNearLossless", "JPEGLSLossy", { EUS_DICOM, EUV_Standard, EUT_TransferSyntax, EUST_other, EUIT_other, UID_PROP_NONE } }, { UID_RLELosslessTransferSyntax, "RLELossless", "RLELossless", { EUS_DICOM, EUV_Standard, EUT_TransferSyntax, EUST_other, EUIT_other, UID_PROP_NONE } }, { UID_DeflatedExplicitVRLittleEndianTransferSyntax, "DeflatedExplicitVRLittleEndian", "DeflatedLittleEndianExplicit", { EUS_DICOM, EUV_Standard, EUT_TransferSyntax, EUST_other, EUIT_other, UID_PROP_NONE } }, + { UID_DeflatedImageFrameCompressionTransferSyntax, "DeflatedImageFrameCompression", "DeflatedImageFrameCompression", { EUS_DICOM, EUV_Standard, EUT_TransferSyntax, EUST_other, EUIT_other, UID_PROP_NONE } }, { UID_JPEG2000LosslessOnlyTransferSyntax, "JPEG2000Lossless", "JPEG2000LosslessOnly", { EUS_DICOM, EUV_Standard, EUT_TransferSyntax, EUST_other, EUIT_other, UID_PROP_NONE } }, { UID_JPEG2000TransferSyntax, "JPEG2000", "JPEG2000", { EUS_DICOM, EUV_Standard, EUT_TransferSyntax, EUST_other, EUIT_other, UID_PROP_NONE } }, { UID_JPEG2000Part2MulticomponentImageCompressionLosslessOnlyTransferSyntax, "JPEG2000MCLossless", "JPEG2000MulticomponentLosslessOnly", { EUS_DICOM, EUV_Standard, EUT_TransferSyntax, EUST_other, EUIT_other, UID_PROP_NONE } }, @@ -327,7 +326,9 @@ static const UIDNameMap uidNameMap[] = { { UID_VLSlideCoordinatesMicroscopicImageStorage, "VLSlideCoordinatesMicroscopicImageStorage", "VLSlideCoordinatesMicroscopicImageStorage", { EUS_DICOM, EUV_Standard, EUT_SOPClass, EUST_Storage, EUIT_Image, UID_PROP_NONE } }, { UID_VLWholeSlideMicroscopyImageStorage, "VLWholeSlideMicroscopyImageStorage", "VLWholeSlideMicroscopyImageStorage", { EUS_DICOM, EUV_Standard, EUT_SOPClass, EUST_Storage, EUIT_Image, UID_PROP_ENHANCED_MF } }, { UID_VolumeRenderingVolumetricPresentationStateStorage, "VolumeRenderingVolumetricPresentationStateStorage", "VolumeRenderingVolumetricPresentationStateStorage", { EUS_DICOM, EUV_Standard, EUT_SOPClass, EUST_Storage, EUIT_PresentationState, UID_PROP_NONE } }, + { UID_WaveformAcquisitionPresentationStateStorage, "WaveformAcquisitionPresentationStateStorage", "WaveformAcquisitionPresentationStateStorage", { EUS_DICOM, EUV_Standard, EUT_SOPClass, EUST_Storage, EUIT_PresentationState, UID_PROP_NONE } }, { UID_WaveformAnnotationSRStorage, "WaveformAnnotationSRStorage", "WaveformAnnotationSRStorage", { EUS_DICOM, EUV_Standard, EUT_SOPClass, EUST_Storage, EUIT_StructuredReport, UID_PROP_NONE } }, + { UID_WaveformPresentationStateStorage, "WaveformPresentationStateStorage", "WaveformPresentationStateStorage", { EUS_DICOM, EUV_Standard, EUT_SOPClass, EUST_Storage, EUIT_PresentationState, UID_PROP_NONE } }, { UID_WideFieldOphthalmicPhotographyStereographicProjectionImageStorage, "WideFieldOphthalmicPhotographyStereographicProjectionImageStorage", "WideFieldOphthalmicPhotographyStereographicProjectionImageStorage", { EUS_DICOM, EUV_Standard, EUT_SOPClass, EUST_Storage, EUIT_Image, UID_PROP_NONE } }, { UID_WideFieldOphthalmicPhotography3DCoordinatesImageStorage, "WideFieldOphthalmicPhotography3DCoordinatesImageStorage", "WideFieldOphthalmicPhotography3DCoordinatesImageStorage", { EUS_DICOM, EUV_Standard, EUT_SOPClass, EUST_Storage, EUIT_Image, UID_PROP_NONE } }, { UID_XADefinedProcedureProtocolStorage, "XADefinedProcedureProtocolStorage", "XADefinedProcedureProtocolStorage", { EUS_DICOM, EUV_Standard, EUT_SOPClass, EUST_Storage, EUIT_other, UID_PROP_NON_PATIENT | UID_PROP_NO_DIR_RECORD } }, @@ -366,6 +367,7 @@ static const UIDNameMap uidNameMap[] = { { UID_DICONDE_EddyCurrentMultiframeImageStorage, "EddyCurrentMultiFrameImageStorage", "DICONDE_EddyCurrentMultiframeImageStorage", { EUS_DICONDE, EUV_Standard, EUT_SOPClass, EUST_Storage, EUIT_Image, UID_PROP_NONE } }, { UID_DICONDE_ThermographyImageStorage, "ThermographyImageStorage", "DICONDE_ThermographyImageStorage", { EUS_DICONDE, EUV_Standard, EUT_SOPClass, EUST_Storage, EUIT_Image, UID_PROP_NONE } }, { UID_DICONDE_ThermographyMultiFrameImageStorage, "ThermographyMultiFrameImageStorage", "DICONDE_ThermographyMultiFrameImageStorage", { EUS_DICONDE, EUV_Standard, EUT_SOPClass, EUST_Storage, EUIT_Image, UID_PROP_NONE } }, + { UID_DICONDE_UltrasoundWaveformStorage, "UltrasoundWaveformStorage", "DICONDE_UltrasoundWaveformStorage", { EUS_DICONDE, EUV_Standard, EUT_SOPClass, EUST_Storage, EUIT_Waveform, UID_PROP_NONE } }, // Query/Retrieve { UID_FINDPatientRootQueryRetrieveInformationModel, "PatientRootQueryRetrieveInformationModelFind", "FINDPatientRootQueryRetrieveInformationModel", { EUS_DICOM, EUV_Standard, EUT_SOPClass, EUST_QueryRetrieve, EUIT_other, UID_PROP_NONE } }, @@ -642,10 +644,7 @@ static const UIDNameMap uidNameMap[] = { { UID_DRAFT_UnifiedProcedureStepPushSOPClass, "UnifiedProcedureStepPushTrial", "DRAFT_UnifiedProcedureStepPushSOPClass", { EUS_DICOM, EUV_Draft, EUT_SOPClass, EUST_other, EUIT_other, UID_PROP_NONE } }, { UID_DRAFT_UnifiedProcedureStepWatchSOPClass, "UnifiedProcedureStepWatchTrial", "DRAFT_UnifiedProcedureStepWatchSOPClass", { EUS_DICOM, EUV_Draft, EUT_SOPClass, EUST_other, EUIT_other, UID_PROP_NONE } }, { UID_DRAFT_UnifiedProcedureStepPullSOPClass, "UnifiedProcedureStepPullTrial", "DRAFT_UnifiedProcedureStepPullSOPClass", { EUS_DICOM, EUV_Draft, EUT_SOPClass, EUST_other, EUIT_other, UID_PROP_NONE } }, - { UID_DRAFT_UnifiedProcedureStepEventSOPClass, "UnifiedProcedureStepEventTrial", "DRAFT_UnifiedProcedureStepEventSOPClass", { EUS_DICOM, EUV_Draft, EUT_SOPClass, EUST_other, EUIT_other, UID_PROP_NONE } }, - - // end of the list - { NULL, NULL, NULL, { EUS_other, EUV_other, EUT_other, EUST_other, EUIT_other, UID_PROP_NONE } } + { UID_DRAFT_UnifiedProcedureStepEventSOPClass, "UnifiedProcedureStepEventTrial", "DRAFT_UnifiedProcedureStepEventSOPClass", { EUS_DICOM, EUV_Draft, EUT_SOPClass, EUST_other, EUIT_other, UID_PROP_NONE } } }; static const int uidNameMap_size = OFstatic_cast(int, sizeof(uidNameMap) / sizeof(UIDNameMap)); @@ -820,7 +819,9 @@ const char* dcmAllStorageSOPClassUIDs[] = { UID_VLSlideCoordinatesMicroscopicImageStorage, UID_VLWholeSlideMicroscopyImageStorage, UID_VolumeRenderingVolumetricPresentationStateStorage, + UID_WaveformAcquisitionPresentationStateStorage, UID_WaveformAnnotationSRStorage, + UID_WaveformPresentationStateStorage, UID_WideFieldOphthalmicPhotographyStereographicProjectionImageStorage, UID_WideFieldOphthalmicPhotography3DCoordinatesImageStorage, UID_XAPerformedProcedureProtocolStorage, @@ -867,6 +868,7 @@ const char* dcmAllStorageSOPClassUIDs[] = { UID_DICONDE_EddyCurrentMultiframeImageStorage, UID_DICONDE_ThermographyImageStorage, UID_DICONDE_ThermographyMultiFrameImageStorage, + UID_DICONDE_UltrasoundWaveformStorage, NULL }; @@ -1066,7 +1068,9 @@ const char* dcmLongSCUStorageSOPClassUIDs[] = { // UID_TractographyResultsStorage, // UID_VariableModalityLUTSoftcopyPresentationStateStorage, // UID_VolumeRenderingVolumetricPresentationStateStorage, +// UID_WaveformAcquisitionPresentationStateStorage, // UID_WaveformAnnotationSRStorage, +// UID_WaveformPresentationStateStorage, // UID_WideFieldOphthalmicPhotographyStereographicProjectionImageStorage, // UID_WideFieldOphthalmicPhotography3DCoordinatesImageStorage, // UID_XAPerformedProcedureProtocolStorage, @@ -1115,6 +1119,7 @@ const char* dcmLongSCUStorageSOPClassUIDs[] = { // UID_DICONDE_EddyCurrentMultiframeImageStorage, // UID_DICONDE_ThermographyImageStorage, // UID_DICONDE_ThermographyMultiFrameImageStorage, +// UID_DICONDE_UltrasoundWaveformStorage, NULL }; @@ -1491,7 +1496,9 @@ static const DcmModalityTable modalities[] = { { UID_VLSlideCoordinatesMicroscopicImageStorage, "VLs", 768 * 576 * 3 }, { UID_VLWholeSlideMicroscopyImageStorage, "VLw", 10000 * 10000 * 3 }, { UID_VolumeRenderingVolumetricPresentationStateStorage, "VPv", 4096 }, + { UID_WaveformAcquisitionPresentationStateStorage, "PSq", 4096 }, { UID_WaveformAnnotationSRStorage, "SRw", 4096 }, + { UID_WaveformPresentationStateStorage, "PSw", 4096 }, { UID_WideFieldOphthalmicPhotographyStereographicProjectionImageStorage, "OWs", 768 * 576 * 3 }, { UID_WideFieldOphthalmicPhotography3DCoordinatesImageStorage, "OW3", 768 * 576 * 3 }, { UID_XADefinedProcedureProtocolStorage, "PPxd", 4096 }, @@ -1536,7 +1543,8 @@ static const DcmModalityTable modalities[] = { { UID_DICONDE_EddyCurrentImageStorage, "EC", 512 * 512 }, { UID_DICONDE_EddyCurrentMultiframeImageStorage, "ECm", 512 * 512 }, { UID_DICONDE_ThermographyImageStorage, "TG", 512 * 512 }, - { UID_DICONDE_ThermographyMultiFrameImageStorage, "TGm", 512 * 512 } + { UID_DICONDE_ThermographyMultiFrameImageStorage, "TGm", 512 * 512 }, + { UID_DICONDE_UltrasoundWaveformStorage, "UW", 4096 } }; static const int numberOfDcmModalityTableEntries = OFstatic_cast(int, sizeof(modalities) / sizeof(DcmModalityTable)); @@ -1546,7 +1554,6 @@ static const int numberOfDcmModalityTableEntries = OFstatic_cast(int, sizeof(mod * Public Function Prototypes */ - const char *dcmSOPClassUIDToModality(const char *sopClassUID, const char *defaultValue) { @@ -1595,6 +1602,7 @@ dcmFindNameOfUID(const char* uid, const char* defaultValue) return defaultValue; } + /* ** dcmFindUIDFromName(const char* name) ** Return the UID of a name. @@ -1632,6 +1640,7 @@ dcmFindKeywordOfUID(const char* uid, const char* defaultValue) return defaultValue; } + /* ** dcmFindUIDFromKeyword(const char* keyword) ** Return the UID of a keyword. diff --git a/dcmdata/libsrc/dcvrds.cc b/dcmdata/libsrc/dcvrds.cc index 29a7ae59..2fa82d93 100644 --- a/dcmdata/libsrc/dcvrds.cc +++ b/dcmdata/libsrc/dcvrds.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2022, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -280,6 +280,8 @@ OFCondition DcmDecimalString::writeXML(STD_NAMESPACE ostream &out, OFCondition DcmDecimalString::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { + OFCondition status = EC_Normal; + /* always write JSON Opener */ writeJsonOpener(out, format); OFBool isValid; @@ -287,11 +289,11 @@ OFCondition DcmDecimalString::writeJson(STD_NAMESPACE ostream &out, if (!isEmpty()) { /* write element value */ - OFString bulkDataValue; - if (format.asBulkDataURI(getTag(), bulkDataValue)) + if (format.asBulkDataURI(getTag(), getLength())) { - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, bulkDataValue); + /* adjust byte order to little endian */ + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); + status = format.writeBulkData(out, getTag(), getLengthField(), byteValues); } else { @@ -300,7 +302,7 @@ OFCondition DcmDecimalString::writeJson(STD_NAMESPACE ostream &out, { OFString value; OFString vmstring = "1"; - OFCondition status = getOFString(value, 0L); + status = getOFString(value, 0L); if (status.bad()) return status; format.printValuePrefix(out); @@ -309,14 +311,28 @@ OFCondition DcmDecimalString::writeJson(STD_NAMESPACE ostream &out, switch (format.getJsonNumStringPolicy()) { case DcmJsonFormat::NSP_auto: - if (isValid) DcmJsonFormat::printNumberDecimal(out, value); - else DcmJsonFormat::printValueString(out, value); + if (isValid) + DcmJsonFormat::printNumberDecimal(out, value); + else + { + DCMDATA_WARN("encountered illegal DS value '" << value << "', converting to JSON string"); + DcmJsonFormat::printValueString(out, value); + } break; case DcmJsonFormat::NSP_always_number: - if (isValid) DcmJsonFormat::printNumberDecimal(out, value); - else return EC_CannotWriteStringAsJsonNumber; + if (isValid) + DcmJsonFormat::printNumberDecimal(out, value); + else + { + DCMDATA_WARN("encountered illegal DS value '" << value << "', aborting conversion to JSON"); + return EC_CannotWriteStringAsJSONNumber; + } break; case DcmJsonFormat::NSP_always_string: + if (!isValid) + { + DCMDATA_WARN("encountered illegal DS value '" << value << "', converting to JSON string"); + } DcmJsonFormat::printValueString(out, value); break; } @@ -332,14 +348,28 @@ OFCondition DcmDecimalString::writeJson(STD_NAMESPACE ostream &out, switch (format.getJsonNumStringPolicy()) { case DcmJsonFormat::NSP_auto: - if (isValid) DcmJsonFormat::printNumberDecimal(out, value); - else DcmJsonFormat::printValueString(out, value); + if (isValid) + DcmJsonFormat::printNumberDecimal(out, value); + else + { + DCMDATA_WARN("encountered illegal DS value '" << value << "', converting to JSON string"); + DcmJsonFormat::printValueString(out, value); + } break; case DcmJsonFormat::NSP_always_number: - if (isValid) DcmJsonFormat::printNumberDecimal(out, value); - else return EC_CannotWriteStringAsJsonNumber; + if (isValid) + DcmJsonFormat::printNumberDecimal(out, value); + else + { + DCMDATA_WARN("encountered illegal DS value '" << value << "', aborting conversion to JSON"); + return EC_CannotWriteStringAsJSONNumber; + } break; case DcmJsonFormat::NSP_always_string: + if (!isValid) + { + DCMDATA_WARN("encountered illegal DS value '" << value << "', converting to JSON string"); + } DcmJsonFormat::printValueString(out, value); break; } @@ -351,8 +381,7 @@ OFCondition DcmDecimalString::writeJson(STD_NAMESPACE ostream &out, /* write JSON Closer */ writeJsonCloser(out, format); - /* always report success */ - return EC_Normal; + return status; } diff --git a/dcmdata/libsrc/dcvrfd.cc b/dcmdata/libsrc/dcvrfd.cc index 182d3994..ce085516 100644 --- a/dcmdata/libsrc/dcvrfd.cc +++ b/dcmdata/libsrc/dcvrfd.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -398,12 +398,14 @@ OFBool DcmFloatingPointDouble::matches(const DcmElement& candidate, OFCondition DcmFloatingPointDouble::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { + OFCondition status = EC_Normal; + /* always write JSON Opener */ writeJsonOpener(out, format); + /* write element value (if non-empty) */ if (!isEmpty()) { - OFCondition status; const unsigned long vm = getVM(); if (! format.getJsonExtensionEnabled()) @@ -419,14 +421,15 @@ OFCondition DcmFloatingPointDouble::writeJson(STD_NAMESPACE ostream &out, } } - OFString value; - if (format.asBulkDataURI(getTag(), value)) + if (format.asBulkDataURI(getTag(), getLength())) { - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, value); + /* adjust byte order to little endian */ + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); + status = format.writeBulkData(out, getTag(), getLengthField(), byteValues); } else { + OFString value; status = getOFString(value, 0L); if (status.bad()) return status; format.printValuePrefix(out); @@ -443,6 +446,5 @@ OFCondition DcmFloatingPointDouble::writeJson(STD_NAMESPACE ostream &out, } /* write JSON Closer */ writeJsonCloser(out, format); - /* always report success */ - return EC_Normal; + return status; } diff --git a/dcmdata/libsrc/dcvrfl.cc b/dcmdata/libsrc/dcvrfl.cc index 78b78894..1b2e5915 100644 --- a/dcmdata/libsrc/dcvrfl.cc +++ b/dcmdata/libsrc/dcvrfl.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2021, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -400,12 +400,14 @@ OFBool DcmFloatingPointSingle::matches(const DcmElement& candidate, OFCondition DcmFloatingPointSingle::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { + OFCondition status = EC_Normal; + /* always write JSON Opener */ writeJsonOpener(out, format); + /* write element value (if non-empty) */ if (!isEmpty()) { - OFCondition status; const unsigned long vm = getVM(); if (! format.getJsonExtensionEnabled()) @@ -421,14 +423,15 @@ OFCondition DcmFloatingPointSingle::writeJson(STD_NAMESPACE ostream &out, } } - OFString value; - if (format.asBulkDataURI(getTag(), value)) + if (format.asBulkDataURI(getTag(), getLength())) { - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, value); + /* adjust byte order to little endian */ + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); + status = format.writeBulkData(out, getTag(), getLengthField(), byteValues); } else { + OFString value; status = getOFString(value, 0L); if (status.bad()) return status; format.printValuePrefix(out); @@ -443,8 +446,8 @@ OFCondition DcmFloatingPointSingle::writeJson(STD_NAMESPACE ostream &out, format.printValueSuffix(out); } } + /* write JSON Closer */ writeJsonCloser(out, format); - /* always report success */ - return EC_Normal; + return status; } diff --git a/dcmdata/libsrc/dcvris.cc b/dcmdata/libsrc/dcvris.cc index 330880db..d94b2373 100644 --- a/dcmdata/libsrc/dcvris.cc +++ b/dcmdata/libsrc/dcvris.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2022, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -147,6 +147,8 @@ OFCondition DcmIntegerString::checkStringValue(const OFString &value, OFCondition DcmIntegerString::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { + OFCondition status = EC_Normal; + /* always write JSON Opener */ writeJsonOpener(out, format); OFBool isValid; @@ -154,11 +156,11 @@ OFCondition DcmIntegerString::writeJson(STD_NAMESPACE ostream &out, if (!isEmpty()) { /* write element value */ - OFString bulkDataValue; - if (format.asBulkDataURI(getTag(), bulkDataValue)) + if (format.asBulkDataURI(getTag(), getLength())) { - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, bulkDataValue); + /* adjust byte order to little endian */ + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); + status = format.writeBulkData(out, getTag(), getLengthField(), byteValues); } else { @@ -167,7 +169,7 @@ OFCondition DcmIntegerString::writeJson(STD_NAMESPACE ostream &out, { OFString value; OFString vmstring = "1"; - OFCondition status = getOFString(value, 0L); + status = getOFString(value, 0L); if (status.bad()) return status; format.printValuePrefix(out); @@ -176,14 +178,27 @@ OFCondition DcmIntegerString::writeJson(STD_NAMESPACE ostream &out, switch (format.getJsonNumStringPolicy()) { case DcmJsonFormat::NSP_auto: - if (isValid) DcmJsonFormat::printNumberInteger(out, value); - else DcmJsonFormat::printValueString(out, value); + if (isValid) + DcmJsonFormat::printNumberInteger(out, value); + else + { + DCMDATA_WARN("encountered illegal IS value '" << value << "', converting to JSON string"); + DcmJsonFormat::printValueString(out, value); + } break; case DcmJsonFormat::NSP_always_number: if (isValid) DcmJsonFormat::printNumberInteger(out, value); - else return EC_CannotWriteStringAsJsonNumber; + else + { + DCMDATA_WARN("encountered illegal IS value '" << value << "', aborting conversion to JSON"); + return EC_CannotWriteStringAsJSONNumber; + } break; case DcmJsonFormat::NSP_always_string: + if (!isValid) + { + DCMDATA_WARN("encountered illegal IS value '" << value << "', converting to JSON string"); + } DcmJsonFormat::printValueString(out, value); break; } @@ -199,14 +214,28 @@ OFCondition DcmIntegerString::writeJson(STD_NAMESPACE ostream &out, switch (format.getJsonNumStringPolicy()) { case DcmJsonFormat::NSP_auto: - if (isValid) DcmJsonFormat::printNumberInteger(out, value); - else DcmJsonFormat::printValueString(out, value); + if (isValid) + DcmJsonFormat::printNumberInteger(out, value); + else + { + DCMDATA_WARN("encountered illegal IS value '" << value << "', converting to JSON string"); + DcmJsonFormat::printValueString(out, value); + } break; case DcmJsonFormat::NSP_always_number: - if (isValid) DcmJsonFormat::printNumberInteger(out, value); - else return EC_CannotWriteStringAsJsonNumber; + if (isValid) + DcmJsonFormat::printNumberInteger(out, value); + else + { + DCMDATA_WARN("encountered illegal IS value '" << value << "', aborting conversion to JSON"); + return EC_CannotWriteStringAsJSONNumber; + } break; case DcmJsonFormat::NSP_always_string: + if (!isValid) + { + DCMDATA_WARN("encountered illegal IS value '" << value << "', converting to JSON string"); + } DcmJsonFormat::printValueString(out, value); break; } @@ -219,5 +248,5 @@ OFCondition DcmIntegerString::writeJson(STD_NAMESPACE ostream &out, /* write JSON Closer */ writeJsonCloser(out, format); /* always report success */ - return EC_Normal; + return status; } diff --git a/dcmdata/libsrc/dcvrobow.cc b/dcmdata/libsrc/dcvrobow.cc index e46618e1..d58ddc01 100644 --- a/dcmdata/libsrc/dcvrobow.cc +++ b/dcmdata/libsrc/dcvrobow.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -98,7 +98,7 @@ int DcmOtherByteOtherWord::compare(const DcmElement& rhs) const * swapping is applied as necessary. */ void* thisData = myThis->getValue(); void* rhsData = myRhs->getValue(); - return memcmp(thisData, rhsData, thisLength); + return compareValues(thisData, rhsData, thisLength); } @@ -848,31 +848,44 @@ OFCondition DcmOtherByteOtherWord::writeXML(STD_NAMESPACE ostream &out, OFCondition DcmOtherByteOtherWord::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { + OFCondition result = EC_Normal; + /* write JSON Opener */ writeJsonOpener(out, format); + /* for an empty value field, we do not need to do anything */ if (getLengthField() > 0) { - OFString value; - if (format.asBulkDataURI(getTag(), value)) - { - /* return defined BulkDataURI */ - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, value); - } - else - { - /* encode binary data as Base64 */ - format.printInlineBinaryPrefix(out); - out << "\""; - /* adjust byte order to little endian */ - Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); - OFStandard::encodeBase64(out, byteValues, OFstatic_cast(size_t, getLengthField())); - out << "\""; - } + /* adjust byte order to little endian */ + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); + result = format.writeBinaryAttribute(out, getTag(), getLengthField(), byteValues); } + /* write JSON Closer */ writeJsonCloser(out, format); - /* always report success */ - return EC_Normal; + return result; +} + + +// ******************************** + +int DcmOtherByteOtherWord::compareValues(const void* myValue, + const void* rhsValue, + const unsigned long valLength) const +{ + /* check for null pointers before comparing */ + if (myValue == OFnullptr || rhsValue == OFnullptr) + { + /* handle null pointers appropriately, e.g., treat null as less than non-null */ + if (myValue == OFnullptr && rhsValue == OFnullptr) + return 0; // both are null, considered equal + else if (myValue == OFnullptr) + return -1; // null is less than non-null + else + return 1; // non-null is greater than null + } + else { + /* Proceed with the comparison */ + return memcmp(myValue, rhsValue, valLength); + } } diff --git a/dcmdata/libsrc/dcvrod.cc b/dcmdata/libsrc/dcvrod.cc index b657ffdb..76970f96 100644 --- a/dcmdata/libsrc/dcvrod.cc +++ b/dcmdata/libsrc/dcvrod.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2013-2020, OFFIS e.V. + * Copyright (C) 2013-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -24,11 +24,13 @@ #include "dcmtk/ofstd/ofuuid.h" #include "dcmtk/ofstd/ofstd.h" +#include "dcmtk/ofstd/ofmath.h" #include "dcmtk/dcmdata/dcvrod.h" #include "dcmtk/dcmdata/dcswap.h" #include "dcmtk/dcmdata/dcjson.h" +#include // ******************************** @@ -95,6 +97,15 @@ unsigned long DcmOtherDouble::getVM() // ******************************** +/* need to check for "Not a Number" in order to avoid possible output of "-nan" */ +static inline void checkAndOutputFloatValue(STD_NAMESPACE ostream &out, + const Float64 value) +{ + if (OFMath::isnan(value)) + out << "nan"; + else + out << value; +} OFCondition DcmOtherDouble::writeXML(STD_NAMESPACE ostream &out, const size_t flags) @@ -140,12 +151,18 @@ OFCondition DcmOtherDouble::writeXML(STD_NAMESPACE ostream &out, { /* increase default precision - see DcmFloatingPointDouble::print() */ const STD_NAMESPACE streamsize oldPrecision = out.precision(17); + /* use the standard "C" locale for proper decimal point */ + const STD_NAMESPACE locale oldLocale = out.imbue(STD_NAMESPACE locale("C")); /* print float values with separators */ - out << (*(floatValues++)); + checkAndOutputFloatValue(out, *(floatValues++)); for (unsigned long i = 1; i < count; i++) - out << "\\" << (*(floatValues++)); - /* reset i/o manipulators */ + { + out << "\\"; + checkAndOutputFloatValue(out, *(floatValues++)); + } + /* reset i/o manipulators and locale */ out.precision(oldPrecision); + out.imbue(oldLocale); } } } @@ -163,33 +180,22 @@ OFCondition DcmOtherDouble::writeXML(STD_NAMESPACE ostream &out, OFCondition DcmOtherDouble::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { - /* always write JSON Opener */ + OFCondition result = EC_Normal; + + /* write JSON Opener */ writeJsonOpener(out, format); + /* for an empty value field, we do not need to do anything */ if (getLengthField() > 0) { - OFString value; - if (format.asBulkDataURI(getTag(), value)) - { - /* return defined BulkDataURI */ - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, value); - } - else - { - /* encode binary data as Base64 */ - format.printInlineBinaryPrefix(out); - out << "\""; - /* adjust byte order to little endian */ - Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); - OFStandard::encodeBase64(out, byteValues, OFstatic_cast(size_t, getLengthField())); - out << "\""; - } + /* adjust byte order to little endian */ + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); + result = format.writeBinaryAttribute(out, getTag(), getLengthField(), byteValues); } - /* write JSON Closer */ + + /* write JSON Closer */ writeJsonCloser(out, format); - /* always report success */ - return EC_Normal; + return result; } diff --git a/dcmdata/libsrc/dcvrof.cc b/dcmdata/libsrc/dcvrof.cc index 2661b0bc..5e1bdeb6 100644 --- a/dcmdata/libsrc/dcvrof.cc +++ b/dcmdata/libsrc/dcvrof.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2020, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -24,11 +24,13 @@ #include "dcmtk/ofstd/ofuuid.h" #include "dcmtk/ofstd/ofstd.h" +#include "dcmtk/ofstd/ofmath.h" #include "dcmtk/dcmdata/dcjson.h" #include "dcmtk/dcmdata/dcvrof.h" #include "dcmtk/dcmdata/dcswap.h" +#include // ******************************** @@ -95,6 +97,15 @@ unsigned long DcmOtherFloat::getVM() // ******************************** +/* need to check for "Not a Number" in order to avoid possible output of "-nan" */ +static inline void checkAndOutputFloatValue(STD_NAMESPACE ostream &out, + const Float32 value) +{ + if (OFMath::isnan(value)) + out << "nan"; + else + out << value; +} OFCondition DcmOtherFloat::writeXML(STD_NAMESPACE ostream &out, const size_t flags) @@ -139,13 +150,19 @@ OFCondition DcmOtherFloat::writeXML(STD_NAMESPACE ostream &out, if (count > 0) { /* increase default precision - see DcmFloatingPointSingle::print() */ - const STD_NAMESPACE streamsize oldPrecision = out.precision(8); + const STD_NAMESPACE streamsize oldPrecision = out.precision(9); + /* use the standard "C" locale for proper decimal point */ + const STD_NAMESPACE locale oldLocale = out.imbue(STD_NAMESPACE locale("C")); /* print float values with separators */ - out << (*(floatValues++)); + checkAndOutputFloatValue(out, *(floatValues++)); for (unsigned long i = 1; i < count; i++) - out << "\\" << (*(floatValues++)); - /* reset i/o manipulators */ + { + out << "\\"; + checkAndOutputFloatValue(out, *(floatValues++)); + } + /* reset i/o manipulators and locale */ out.precision(oldPrecision); + out.imbue(oldLocale); } } } @@ -163,33 +180,22 @@ OFCondition DcmOtherFloat::writeXML(STD_NAMESPACE ostream &out, OFCondition DcmOtherFloat::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { - /* always write JSON Opener */ + OFCondition result = EC_Normal; + + /* write JSON Opener */ writeJsonOpener(out, format); + /* for an empty value field, we do not need to do anything */ if (getLengthField() > 0) { - OFString value; - if (format.asBulkDataURI(getTag(), value)) - { - /* return defined BulkDataURI */ - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, value); - } - else - { - /* encode binary data as Base64 */ - format.printInlineBinaryPrefix(out); - out << "\""; - /* adjust byte order to little endian */ - Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); - OFStandard::encodeBase64(out, byteValues, OFstatic_cast(size_t, getLengthField())); - out << "\""; - } + /* adjust byte order to little endian */ + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); + result = format.writeBinaryAttribute(out, getTag(), getLengthField(), byteValues); } - /* always write JSON Closer */ + + /* write JSON Closer */ writeJsonCloser(out, format); - /* always report success */ - return EC_Normal; + return result; } diff --git a/dcmdata/libsrc/dcvrol.cc b/dcmdata/libsrc/dcvrol.cc index 5a0a3798..d9e137c5 100644 --- a/dcmdata/libsrc/dcvrol.cc +++ b/dcmdata/libsrc/dcvrol.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2016-2019, OFFIS e.V. + * Copyright (C) 2016-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -165,33 +165,22 @@ OFCondition DcmOtherLong::writeXML(STD_NAMESPACE ostream &out, OFCondition DcmOtherLong::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { + OFCondition result = EC_Normal; + /* write JSON Opener */ writeJsonOpener(out, format); + /* for an empty value field, we do not need to do anything */ if (getLengthField() > 0) { - OFString value; - if (format.asBulkDataURI(getTag(), value)) - { - /* return defined BulkDataURI */ - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, value); - } - else - { - /* encode binary data as Base64 */ - format.printInlineBinaryPrefix(out); - out << "\""; - /* adjust byte order to little endian */ - Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); - OFStandard::encodeBase64(out, byteValues, OFstatic_cast(size_t, getLengthField())); - out << "\""; - } + /* adjust byte order to little endian */ + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); + result = format.writeBinaryAttribute(out, getTag(), getLengthField(), byteValues); } + /* write JSON Closer */ writeJsonCloser(out, format); - /* always report success */ - return EC_Normal; + return result; } diff --git a/dcmdata/libsrc/dcvrov.cc b/dcmdata/libsrc/dcvrov.cc index 59d14566..9ade5704 100644 --- a/dcmdata/libsrc/dcvrov.cc +++ b/dcmdata/libsrc/dcvrov.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019, OFFIS e.V. + * Copyright (C) 2019-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -165,33 +165,22 @@ OFCondition DcmOther64bitVeryLong::writeXML(STD_NAMESPACE ostream &out, OFCondition DcmOther64bitVeryLong::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { + OFCondition result = EC_Normal; + /* write JSON Opener */ writeJsonOpener(out, format); + /* for an empty value field, we do not need to do anything */ if (getLengthField() > 0) { - OFString value; - if (format.asBulkDataURI(getTag(), value)) - { - /* return defined BulkDataURI */ - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, value); - } - else - { - /* encode binary data as Base64 */ - format.printInlineBinaryPrefix(out); - out << "\""; - /* adjust byte order to little endian */ - Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); - OFStandard::encodeBase64(out, byteValues, OFstatic_cast(size_t, getLengthField())); - out << "\""; - } + /* adjust byte order to little endian */ + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); + result = format.writeBinaryAttribute(out, getTag(), getLengthField(), byteValues); } + /* write JSON Closer */ writeJsonCloser(out, format); - /* always report success */ - return EC_Normal; + return result; } diff --git a/dcmdata/libsrc/dcvrpobw.cc b/dcmdata/libsrc/dcvrpobw.cc index fac45489..bcb8e06a 100644 --- a/dcmdata/libsrc/dcvrpobw.cc +++ b/dcmdata/libsrc/dcvrpobw.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1997-2020, OFFIS e.V. + * Copyright (C) 1997-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -86,7 +86,7 @@ int DcmPolymorphOBOW::compare(const DcmElement& rhs) const // Get values, always compare in Little Endian byte order (only relevant for OW) void* myValue = myThis->getValue(EBO_LittleEndian); void* rhsValue = myRhs->getValue(EBO_LittleEndian); - result = memcmp(myValue, rhsValue, myLength); + result = compareValues(myValue, rhsValue, myLength); if (result < 0) return -1; else if (result > 0) diff --git a/dcmdata/libsrc/dcvrsv.cc b/dcmdata/libsrc/dcvrsv.cc index 8ced07cf..673726f4 100644 --- a/dcmdata/libsrc/dcvrsv.cc +++ b/dcmdata/libsrc/dcvrsv.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019-2024, OFFIS e.V. + * Copyright (C) 2019-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -399,6 +399,8 @@ OFCondition DcmSigned64bitVeryLong::verify(const OFBool autocorrect) OFCondition DcmSigned64bitVeryLong::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { + OFCondition status = EC_Normal; + /* always write JSON Opener */ writeJsonOpener(out, format); @@ -406,11 +408,11 @@ OFCondition DcmSigned64bitVeryLong::writeJson(STD_NAMESPACE ostream &out, { /* write element value */ - OFString bulkDataValue; - if (format.asBulkDataURI(getTag(), bulkDataValue)) + if (format.asBulkDataURI(getTag(), getLength())) { - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, bulkDataValue); + /* adjust byte order to little endian */ + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); + status = format.writeBulkData(out, getTag(), getLengthField(), byteValues); } else { @@ -418,7 +420,7 @@ OFCondition DcmSigned64bitVeryLong::writeJson(STD_NAMESPACE ostream &out, OFString value; Sint64 v = 0; - OFCondition status = getOFString(value, 0L); + status = getOFString(value, 0L); if (status.bad()) return status; format.printValuePrefix(out); @@ -448,6 +450,5 @@ OFCondition DcmSigned64bitVeryLong::writeJson(STD_NAMESPACE ostream &out, /* write JSON Closer */ writeJsonCloser(out, format); - /* always report success */ - return EC_Normal; + return status; } diff --git a/dcmdata/libsrc/dcvruv.cc b/dcmdata/libsrc/dcvruv.cc index 364acb44..1f72a027 100644 --- a/dcmdata/libsrc/dcvruv.cc +++ b/dcmdata/libsrc/dcvruv.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019-2024, OFFIS e.V. + * Copyright (C) 2019-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -398,6 +398,8 @@ OFCondition DcmUnsigned64bitVeryLong::verify(const OFBool autocorrect) OFCondition DcmUnsigned64bitVeryLong::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { + OFCondition status = EC_Normal; + /* always write JSON Opener */ writeJsonOpener(out, format); @@ -405,11 +407,11 @@ OFCondition DcmUnsigned64bitVeryLong::writeJson(STD_NAMESPACE ostream &out, { /* write element value */ - OFString bulkDataValue; - if (format.asBulkDataURI(getTag(), bulkDataValue)) + if (format.asBulkDataURI(getTag(), getLength())) { - format.printBulkDataURIPrefix(out); - DcmJsonFormat::printString(out, bulkDataValue); + /* adjust byte order to little endian */ + Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); + status = format.writeBulkData(out, getTag(), getLengthField(), byteValues); } else { @@ -417,7 +419,7 @@ OFCondition DcmUnsigned64bitVeryLong::writeJson(STD_NAMESPACE ostream &out, OFString value; Uint64 v = 0; - OFCondition status = getOFString(value, 0L); + status = getOFString(value, 0L); if (status.bad()) return status; format.printValuePrefix(out); @@ -447,6 +449,5 @@ OFCondition DcmUnsigned64bitVeryLong::writeJson(STD_NAMESPACE ostream &out, /* write JSON Closer */ writeJsonCloser(out, format); - /* always report success */ - return EC_Normal; + return status; } diff --git a/dcmdata/libsrc/dcxfer.cc b/dcmdata/libsrc/dcxfer.cc index 166f3a92..acb8da08 100644 --- a/dcmdata/libsrc/dcxfer.cc +++ b/dcmdata/libsrc/dcxfer.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -24,6 +24,9 @@ #include "dcmtk/dcmdata/dcuid.h" #include +#define DCXFER_DEFAULT_MIME_TYPE "application/dicom" +#define DCXFER_DEFAULT_EXTENSION ".bin" + typedef struct { const char *xferID; @@ -39,6 +42,8 @@ typedef struct Uint32 JPEGProcess12; E_StreamCompression streamCompression; E_XferValidity xferValidity; + const char *mimeType; + const char *filenameExtension; } S_XferNames; @@ -59,7 +64,9 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + DCXFER_DEFAULT_MIME_TYPE, + DCXFER_DEFAULT_EXTENSION }, // entry #1 { "", // internal type, no UID defined @@ -73,7 +80,9 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Internal + EXV_Internal, + DCXFER_DEFAULT_MIME_TYPE, + DCXFER_DEFAULT_EXTENSION }, // entry #2 { UID_LittleEndianExplicitTransferSyntax, @@ -87,7 +96,9 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + DCXFER_DEFAULT_MIME_TYPE, + DCXFER_DEFAULT_EXTENSION }, // entry #3 { UID_BigEndianExplicitTransferSyntax, @@ -101,7 +112,9 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Retired // retired with Supplement 98 + EXV_Retired, // retired with Supplement 98 + DCXFER_DEFAULT_MIME_TYPE, + DCXFER_DEFAULT_EXTENSION }, // entry #4 { UID_EncapsulatedUncompressedExplicitVRLittleEndianTransferSyntax, @@ -115,7 +128,9 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + DCXFER_DEFAULT_MIME_TYPE, + DCXFER_DEFAULT_EXTENSION }, // entry #5 { UID_JPEGProcess1TransferSyntax, @@ -129,7 +144,9 @@ const S_XferNames XferNames[] = OFTrue, 1L, 1L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jpeg", + ".jpeg" }, // entry #6 { UID_JPEGProcess2_4TransferSyntax, @@ -143,7 +160,9 @@ const S_XferNames XferNames[] = OFTrue, 2L, 4L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jpeg", + ".jpeg" }, // entry #7 { UID_JPEGProcess3_5TransferSyntax, @@ -157,7 +176,9 @@ const S_XferNames XferNames[] = OFTrue, 3L, 5L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #8 { UID_JPEGProcess6_8TransferSyntax, @@ -171,7 +192,9 @@ const S_XferNames XferNames[] = OFTrue, 6L, 8L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #9 { UID_JPEGProcess7_9TransferSyntax, @@ -185,7 +208,9 @@ const S_XferNames XferNames[] = OFTrue, 7L, 9L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #10 { UID_JPEGProcess10_12TransferSyntax, @@ -199,7 +224,9 @@ const S_XferNames XferNames[] = OFTrue, 10L, 12L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #11 { UID_JPEGProcess11_13TransferSyntax, @@ -213,7 +240,9 @@ const S_XferNames XferNames[] = OFTrue, 11L, 13L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #12 { UID_JPEGProcess14TransferSyntax, @@ -227,7 +256,9 @@ const S_XferNames XferNames[] = OFTrue, 14, 14L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jpeg", + ".jpeg" }, // entry #13 { UID_JPEGProcess15TransferSyntax, @@ -241,7 +272,9 @@ const S_XferNames XferNames[] = OFTrue, 15L, 15L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #14 { UID_JPEGProcess16_18TransferSyntax, @@ -255,7 +288,9 @@ const S_XferNames XferNames[] = OFTrue, 16L, 18L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #15 { UID_JPEGProcess17_19TransferSyntax, @@ -269,7 +304,9 @@ const S_XferNames XferNames[] = OFTrue, 17L, 19L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #16 { UID_JPEGProcess20_22TransferSyntax, @@ -283,7 +320,9 @@ const S_XferNames XferNames[] = OFTrue, 20L, 22L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #17 { UID_JPEGProcess21_23TransferSyntax, @@ -297,7 +336,9 @@ const S_XferNames XferNames[] = OFTrue, 21L, 23L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #18 { UID_JPEGProcess24_26TransferSyntax, @@ -311,7 +352,9 @@ const S_XferNames XferNames[] = OFTrue, 24L, 26L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #19 { UID_JPEGProcess25_27TransferSyntax, @@ -325,7 +368,9 @@ const S_XferNames XferNames[] = OFTrue, 25L, 27L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #20 { UID_JPEGProcess28TransferSyntax, @@ -339,7 +384,9 @@ const S_XferNames XferNames[] = OFTrue, 28L, 28L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #21 { UID_JPEGProcess29TransferSyntax, @@ -353,7 +400,9 @@ const S_XferNames XferNames[] = OFTrue, 29L, 29L, ESC_none, - EXV_Retired // retired with Supplement 61 + EXV_Retired, // retired with Supplement 61 + "image/jpeg", + ".jpeg" }, // entry #22 { UID_JPEGProcess14SV1TransferSyntax, @@ -367,7 +416,9 @@ const S_XferNames XferNames[] = OFTrue, 14L, 14L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jpeg", + ".jpeg" }, // entry #23 { UID_RLELosslessTransferSyntax, @@ -381,7 +432,9 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "image/dicom-rle", + ".dicom-rle" }, // entry #24 { UID_DeflatedExplicitVRLittleEndianTransferSyntax, @@ -399,9 +452,27 @@ const S_XferNames XferNames[] = #else ESC_unsupported, #endif - EXV_Standard + EXV_Standard, + DCXFER_DEFAULT_MIME_TYPE, + DCXFER_DEFAULT_EXTENSION }, // entry #25 + { UID_DeflatedImageFrameCompressionTransferSyntax, + "Deflated Image Frame Compression", + EXS_DeflatedImageFrameCompression, + EBO_LittleEndian, + EBO_LittleEndian, + EVT_Explicit, + EPE_Encapsulated, + EPC_LosslessCompressed, + OFFalse, + 0L, 0L, + ESC_none, + EXV_Standard, + "application/x-deflate", + ".x-deflate" + }, + // entry #26 { UID_JPEGLSLosslessTransferSyntax, "JPEG-LS Lossless", EXS_JPEGLSLossless, @@ -413,9 +484,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jls", + ".jls" }, - // entry #26 + // entry #27 { UID_JPEGLSLossyTransferSyntax, "JPEG-LS Lossy (Near-lossless)", EXS_JPEGLSLossy, @@ -427,9 +500,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jls", + ".jls" }, - // entry #27 + // entry #28 { UID_JPEG2000LosslessOnlyTransferSyntax, "JPEG 2000 (Lossless only)", EXS_JPEG2000LosslessOnly, @@ -441,9 +516,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jp2", + ".jp2" }, - // entry #28 + // entry #29 { UID_JPEG2000TransferSyntax, "JPEG 2000 (Lossless or Lossy)", EXS_JPEG2000, @@ -455,9 +532,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jp2", + ".jp2" }, - // entry #29 + // entry #30 { UID_JPEG2000Part2MulticomponentImageCompressionLosslessOnlyTransferSyntax, "JPEG 2000 Part 2 Multicomponent Image Compression (Lossless only)", EXS_JPEG2000MulticomponentLosslessOnly, @@ -469,9 +548,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jpx", + ".jpx" }, - // entry #30 + // entry #31 { UID_JPEG2000Part2MulticomponentImageCompressionTransferSyntax, "JPEG 2000 Part 2 Multicomponent Image Compression (Lossless or Lossy)", EXS_JPEG2000Multicomponent, @@ -483,9 +564,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jpx", + ".jpx" }, - // entry #31 + // entry #32 { UID_JPIPReferencedTransferSyntax, "JPIP Referenced", EXS_JPIPReferenced, @@ -497,9 +580,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + DCXFER_DEFAULT_MIME_TYPE, + DCXFER_DEFAULT_EXTENSION }, - // entry #32 + // entry #33 { UID_JPIPReferencedDeflateTransferSyntax, "JPIP Referenced Deflate", EXS_JPIPReferencedDeflate, @@ -515,9 +600,11 @@ const S_XferNames XferNames[] = #else ESC_unsupported, #endif - EXV_Standard + EXV_Standard, + DCXFER_DEFAULT_MIME_TYPE, + DCXFER_DEFAULT_EXTENSION }, - // entry #33 + // entry #34 { UID_MPEG2MainProfileAtMainLevelTransferSyntax, "MPEG2 Main Profile @ Main Level", // changed with DICOM 2016e to: MPEG2 Main Profile / Main Level EXS_MPEG2MainProfileAtMainLevel, @@ -529,9 +616,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mpeg", + ".mpeg" }, - // entry #34 + // entry #35 { UID_FragmentableMPEG2MainProfileMainLevelTransferSyntax, "Fragmentable MPEG2 Main Profile / Main Level", EXS_FragmentableMPEG2MainProfileMainLevel, @@ -543,9 +632,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mpeg", + ".mpeg" }, - // entry #35 + // entry #36 { UID_MPEG2MainProfileAtHighLevelTransferSyntax, "MPEG2 Main Profile @ High Level", // changed with DICOM 2016e to: MPEG2 Main Profile / High Level EXS_MPEG2MainProfileAtHighLevel, @@ -557,9 +648,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mpeg", + ".mpeg" }, - // entry #36 + // entry #37 { UID_FragmentableMPEG2MainProfileHighLevelTransferSyntax, "Fragmentable MPEG2 Main Profile / High Level", EXS_FragmentableMPEG2MainProfileHighLevel, @@ -571,9 +664,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mpeg", + ".mpeg" }, - // entry #37 + // entry #38 { UID_MPEG4HighProfileLevel4_1TransferSyntax, "MPEG-4 AVC/H.264 High Profile / Level 4.1", EXS_MPEG4HighProfileLevel4_1, @@ -585,9 +680,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mp4", + ".mp4" }, - // entry #38 + // entry #39 { UID_FragmentableMPEG4HighProfileLevel4_1TransferSyntax, "Fragmentable MPEG-4 AVC/H.264 High Profile / Level 4.1", EXS_FragmentableMPEG4HighProfileLevel4_1, @@ -599,9 +696,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mp4", + ".mp4" }, - // entry #39 + // entry #40 { UID_MPEG4BDcompatibleHighProfileLevel4_1TransferSyntax, "MPEG-4 AVC/H.264 BD-compatible High Profile / Level 4.1", EXS_MPEG4BDcompatibleHighProfileLevel4_1, @@ -613,9 +712,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mp4", + ".mp4" }, - // entry #40 + // entry #41 { UID_FragmentableMPEG4BDcompatibleHighProfileLevel4_1TransferSyntax, "Fragmentable MPEG-4 AVC/H.264 BD-compatible High Profile / Level 4.1", EXS_FragmentableMPEG4BDcompatibleHighProfileLevel4_1, @@ -627,9 +728,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mp4", + ".mp4" }, - // entry #41 + // entry #42 { UID_MPEG4HighProfileLevel4_2_For2DVideoTransferSyntax, "MPEG-4 AVC/H.264 High Profile / Level 4.2 For 2D Video", EXS_MPEG4HighProfileLevel4_2_For2DVideo, @@ -641,9 +744,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mp4", + ".mp4" }, - // entry #42 + // entry #43 { UID_FragmentableMPEG4HighProfileLevel4_2_For2DVideoTransferSyntax, "Fragmentable MPEG-4 AVC/H.264 High Profile / Level 4.2 For 2D Video", EXS_FragmentableMPEG4HighProfileLevel4_2_For2DVideo, @@ -655,9 +760,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mp4", + ".mp4" }, - // entry #43 + // entry #44 { UID_MPEG4HighProfileLevel4_2_For3DVideoTransferSyntax, "MPEG-4 AVC/H.264 High Profile / Level 4.2 For 3D Video", EXS_MPEG4HighProfileLevel4_2_For3DVideo, @@ -669,9 +776,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mp4", + ".mp4" }, - // entry #44 + // entry #45 { UID_FragmentableMPEG4HighProfileLevel4_2_For3DVideoTransferSyntax, "Fragmentable MPEG-4 AVC/H.264 High Profile / Level 4.2 For 3D Video", EXS_FragmentableMPEG4HighProfileLevel4_2_For3DVideo, @@ -683,9 +792,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mp4", + ".mp4" }, - // entry #45 + // entry #46 { UID_MPEG4StereoHighProfileLevel4_2TransferSyntax, "MPEG-4 AVC/H.264 Stereo High Profile / Level 4.2", EXS_MPEG4StereoHighProfileLevel4_2, @@ -697,9 +808,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mp4", + ".mp4" }, - // entry #46 + // entry #47 { UID_FragmentableMPEG4StereoHighProfileLevel4_2TransferSyntax, "Fragmentable MPEG-4 AVC/H.264 Stereo High Profile / Level 4.2", EXS_FragmentableMPEG4StereoHighProfileLevel4_2, @@ -711,9 +824,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/mp4", + ".mp4" }, - // entry #47 + // entry #48 { UID_HEVCMainProfileLevel5_1TransferSyntax, "HEVC/H.265 Main Profile / Level 5.1", EXS_HEVCMainProfileLevel5_1, @@ -725,9 +840,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/H265", + ".H265" }, - // entry #48 + // entry #49 { UID_HEVCMain10ProfileLevel5_1TransferSyntax, "HEVC/H.265 Main 10 Profile / Level 5.1", EXS_HEVCMain10ProfileLevel5_1, @@ -739,9 +856,11 @@ const S_XferNames XferNames[] = OFTrue, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "video/H265", + ".H265" }, - // entry #49 + // entry #50 { UID_JPEGXLLosslessTransferSyntax, "JPEG XL Lossless", EXS_JPEGXLLossless, @@ -753,9 +872,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jxl", + ".jxl" }, - // entry #50 + // entry #51 { UID_JPEGXLJPEGRecompressionTransferSyntax, "JPEG XL JPEG Recompression", EXS_JPEGXLJPEGRecompression, @@ -767,9 +888,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jxl", + ".jxl" }, - // entry #51 + // entry #52 { UID_JPEGXLTransferSyntax, "JPEG XL", EXS_JPEGXL, @@ -781,9 +904,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jxl", + ".jxl" }, - // entry #52 + // entry #53 { UID_HighThroughputJPEG2000ImageCompressionLosslessOnlyTransferSyntax, "High-Throughput JPEG 2000 Image Compression (Lossless Only)", EXS_HighThroughputJPEG2000LosslessOnly, @@ -795,9 +920,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jphc", + ".jphc" }, - // entry #53 + // entry #54 { UID_HighThroughputJPEG2000RPCLImageCompressionLosslessOnlyTransferSyntax, "High-Throughput JPEG 2000 with RPCL Options Image Compression (Lossless Only)", EXS_HighThroughputJPEG2000withRPCLOptionsLosslessOnly, @@ -809,9 +936,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jphc", + ".jphc" }, - // entry #54 + // entry #55 { UID_HighThroughputJPEG2000ImageCompressionTransferSyntax, "High-Throughput JPEG 2000 Image Compression", EXS_HighThroughputJPEG2000, @@ -823,9 +952,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + "image/jphc", + ".jphc" }, - // entry #55 + // entry #56 { UID_JPIPHTJ2KReferencedTransferSyntax, "JPIP HTJ2K Referenced", EXS_JPIPHTJ2KReferenced, @@ -837,9 +968,11 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Standard + EXV_Standard, + DCXFER_DEFAULT_MIME_TYPE, + DCXFER_DEFAULT_EXTENSION }, - // entry #56 + // entry #57 { UID_JPIPHTJ2KReferencedDeflateTransferSyntax, "JPIP HTJ2K Referenced Deflate", EXS_JPIPHTJ2KReferencedDeflate, @@ -855,9 +988,11 @@ const S_XferNames XferNames[] = #else ESC_unsupported, #endif - EXV_Standard + EXV_Standard, + DCXFER_DEFAULT_MIME_TYPE, + DCXFER_DEFAULT_EXTENSION }, - // entry #57 + // entry #58 { UID_PrivateGE_LEI_WithBigEndianPixelDataTransferSyntax, "Private GE Little Endian Implicit with big endian pixel data", EXS_PrivateGE_LEI_WithBigEndianPixelData, @@ -869,7 +1004,9 @@ const S_XferNames XferNames[] = OFFalse, 0L, 0L, ESC_none, - EXV_Private + EXV_Private, + DCXFER_DEFAULT_MIME_TYPE, + DCXFER_DEFAULT_EXTENSION } }; @@ -892,7 +1029,9 @@ DcmXfer::DcmXfer(E_TransferSyntax xfer) JPEGProcess8(0L), JPEGProcess12(0L), streamCompression(ESC_none), - xferValidity(EXV_unknown) + xferValidity(EXV_unknown), + mimeType(DCXFER_DEFAULT_MIME_TYPE), + filenameExtension(DCXFER_DEFAULT_EXTENSION) { /* casting the enum to an integer should be safe */ const int i = OFstatic_cast(int, xfer); @@ -912,6 +1051,8 @@ DcmXfer::DcmXfer(E_TransferSyntax xfer) JPEGProcess12 = XferNames[i].JPEGProcess12; streamCompression = XferNames[i].streamCompression; xferValidity = XferNames[i].xferValidity; + mimeType = XferNames[i].mimeType; + filenameExtension = XferNames[i].filenameExtension; } } @@ -932,7 +1073,9 @@ DcmXfer::DcmXfer(const char *xferName_xferID) JPEGProcess8(0L), JPEGProcess12(0L), streamCompression(ESC_none), - xferValidity(EXV_unknown) + xferValidity(EXV_unknown), + mimeType(DCXFER_DEFAULT_MIME_TYPE), + filenameExtension(DCXFER_DEFAULT_EXTENSION) { if (xferName_xferID != NULL) { @@ -961,6 +1104,8 @@ DcmXfer::DcmXfer(const char *xferName_xferID) JPEGProcess12 = XferNames[i].JPEGProcess12; streamCompression = XferNames[i].streamCompression; xferValidity = XferNames[i].xferValidity; + mimeType = XferNames[i].mimeType; + filenameExtension = XferNames[i].filenameExtension; } } } @@ -982,7 +1127,9 @@ DcmXfer::DcmXfer(const DcmXfer &newXfer) JPEGProcess8(newXfer.JPEGProcess8), JPEGProcess12(newXfer.JPEGProcess12), streamCompression(newXfer.streamCompression), - xferValidity(newXfer.xferValidity) + xferValidity(newXfer.xferValidity), + mimeType(newXfer.mimeType), + filenameExtension(newXfer.filenameExtension) { } @@ -1018,6 +1165,8 @@ DcmXfer &DcmXfer::operator=(const E_TransferSyntax xfer) JPEGProcess12 = XferNames[i].JPEGProcess12; streamCompression = XferNames[i].streamCompression; xferValidity = XferNames[i].xferValidity; + mimeType = XferNames[i].mimeType; + filenameExtension = XferNames[i].filenameExtension; } else { xferSyn = EXS_Unknown; xferID = ""; @@ -1032,6 +1181,8 @@ DcmXfer &DcmXfer::operator=(const E_TransferSyntax xfer) JPEGProcess12 = 0L; streamCompression = ESC_none; xferValidity = EXV_unknown; + mimeType = DCXFER_DEFAULT_MIME_TYPE; + filenameExtension = DCXFER_DEFAULT_EXTENSION; } return *this; } @@ -1057,6 +1208,8 @@ DcmXfer &DcmXfer::operator=(const DcmXfer &newXfer) JPEGProcess12 = newXfer.JPEGProcess12; streamCompression = newXfer.streamCompression; xferValidity = newXfer.xferValidity; + mimeType = newXfer.mimeType; + filenameExtension = newXfer.filenameExtension; } return *this; } diff --git a/dcmdata/libsrc/vrscanl.c b/dcmdata/libsrc/vrscanl.c index c81ce71a..36f5e33a 100644 --- a/dcmdata/libsrc/vrscanl.c +++ b/dcmdata/libsrc/vrscanl.c @@ -573,7 +573,7 @@ static const YY_CHAR yy_meta[42] = static const flex_int16_t yy_base[2631] = { 0, - 0, 38, 1130,16740,16740, 0, 1088, 6, 1083, 7, + 0, 38, 1130,16752,16752, 0, 1088, 6, 1083, 7, 1053, 1048, 1024, 6, 0, 1019, 1, 36, 46, 38, 74, 47, 0, 993, 75, 92, 0, 50, 0, 51, 89, 90, 0, 994, 93, 100, 101, 0, 0, 984, @@ -750,7 +750,7 @@ static const flex_int16_t yy_base[2631] = 5407, 0, 5041, 5398, 5412, 5419, 5426, 5431, 4, 5435, 5440, 5445, 5450, 5454, 5459, 5464, 5469, 5473, 5478, 5483, - 5488, 5492, 5497,16740, 5522, 5537, 5547, 5559, 5567, 5579, + 5488, 5492, 5497,16752, 5522, 5537, 5547, 5559, 5567, 5579, 5587, 5600, 5616, 5631, 5646, 5656, 5667, 5681, 5695, 5705, 5708, 5720, 5728, 5731, 5739, 5746, 5760, 5768, 5771, 5779, 5786, 5798, 5811, 5826, 5841, 5856, 5866, 5870, 5881, 5893, @@ -855,13 +855,13 @@ static const flex_int16_t yy_base[2631] = 16013,16025,16037,16049,16061,16073,16085,16097,16109,16121, 16133,16145,16157,16169,16181,16193,16205,16217,16229,16241, 16253,16265,16277,16289,16301,16309,16316,16324,16327,16330, - 16333, 5307, 5316,16340, 5320, 5381,16352,16360,16363,16370, - 16378,16385,16393,16396,16399,16402, 5385, 5397,16409, 5420, - 5497,16421,16429,16436,16444,16447,16450,16453,16456,16459, + 16333,16336, 5307,16343,16351, 5316,16358,16366,16369,16376, + 16384,16391,16399,16402,16405,16408,16411, 5320,16418,16426, + 5381,16433,16441,16448,16456,16459,16462,16465,16468,16471, - 16462,16465,16468,16471,16474,16477,16480,16487,16499,16507, - 16510,16517,16529,16541,16553,16561,16568,16580,16592,16604, - 16616,16628,16640,16652,16664,16676,16688,16700,16712,16724 + 16474,16477,16480,16483,16486,16489,16492,16499,16511,16519, + 16522,16529,16541,16553,16565,16573,16580,16592,16604,16616, + 16628,16640,16652,16664,16676,16688,16700,16712,16724,16736 } ; static const flex_int16_t yy_def[2631] = @@ -1157,7 +1157,7 @@ static const flex_int16_t yy_def[2631] = 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614 } ; -static const flex_int16_t yy_nxt[16782] = +static const flex_int16_t yy_nxt[16794] = { 0, 1614, 1614, 5, 1614, 30, 35, 1614, 1614, 1614, 1614, 110, 111, 112, 147, 147, 147, 147, 88, 89, 104, @@ -1743,19 +1743,19 @@ static const flex_int16_t yy_nxt[16782] = 1547, 1547, 1547, 1547, 1547, 1547, 1547, 486, 242, 1505, 1506, 1556, 1556, 1556, 1556, 1557, 489, 350, 1508, 1509, - 365, 1614, 1414, 1415, 1566, 1518, 1518, 368, 245, 369, - 1614, 1417, 1418, 1568, 1520, 1520, 50, 245, 1523, 1523, + 365, 1614, 1414, 1415, 1566, 1520, 1520, 368, 245, 369, + 1614, 1417, 1418, 1568, 1525, 1525, 50, 245, 1553, 1553, 368, 1569, 1569, 1569, 1569, 1569, 1569, 1614, 350, 50, 1569, 1569, 1569, 1569, 1569, 1569, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 245, 365, 369, 486, 1585, 1505, 1506, 1582, 489, 486, 1508, 1509, 1584, 365, 489, 1414, 1415, 369, 1585, 1417, 1418, 368, - 50, 368, 1586, 1592, 1592, 1593, 50, 368, 1594, 1525, - 1525, 368, 50, 1551, 1551, 50, 1586, 365, 1586, 1414, + 50, 368, 1586, 1592, 1592, 1593, 50, 368, 1594, 1558, + 1558, 368, 50, 1614, 1614, 50, 1586, 365, 1586, 1414, - 1415, 369, 1594, 1417, 1418, 1553, 1553, 486, 1586, 1505, + 1415, 369, 1594, 1417, 1418, 1614, 1614, 486, 1586, 1505, 1506, 489, 1614, 1508, 1509, 1614, 365, 368, 1414, 1415, - 1614, 50, 1586, 369, 1614, 1417, 1418, 368, 1556, 1556, + 1614, 50, 1586, 369, 1614, 1417, 1418, 368, 1614, 1614, 486, 50, 1505, 1506, 1614, 489, 368, 1508, 1509, 365, 1614, 1414, 1415, 50, 369, 1614, 1417, 1418, 1614, 486, 368, 1505, 1506, 1614, 489, 50, 1508, 1509, 365, 368, @@ -1764,7 +1764,7 @@ static const flex_int16_t yy_nxt[16782] = 1415, 1614, 369, 50, 1417, 1418, 1614, 486, 368, 1505, 1506, 1614, 489, 50, 1508, 1509, 486, 368, 1505, 1506, - 1614, 489, 50, 1508, 1509, 1558, 1558, 368, 1614, 1614, + 1614, 489, 50, 1508, 1509, 1614, 1614, 368, 1614, 1614, 1614, 1614, 50, 1614, 1614, 1614, 368, 1614, 1614, 1614, 1614, 50, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, @@ -2956,58 +2956,60 @@ static const flex_int16_t yy_nxt[16782] = 1511, 1614, 1614, 1614, 1614, 1511, 1512, 1512, 1512, 1513, 1614, 1614, 1614, 1513, 1513, 1513, 1614, 1614, 1614, 1614, 1513, 1514, 1514, 1514, 1515, 1515, 1515, 1516, 1516, 1516, - 1517, 1517, 1517, 1522, 1614, 1522, 1614, 1522, 1522, 1614, - 1614, 1614, 1614, 1614, 1522, 1527, 1614, 1527, 1614, 1527, - 1527, 1614, 1614, 1614, 1614, 1614, 1527, 1532, 1532, 1532, - 1537, 1537, 1537, 1538, 1614, 1614, 1614, 1538, 1538, 1538, - 1614, 1614, 1614, 1614, 1538, 1539, 1539, 1539, 1540, 1614, - 1614, 1614, 1540, 1540, 1540, 1614, 1614, 1614, 1614, 1540, - - 1541, 1541, 1541, 1542, 1542, 1542, 1546, 1546, 1546, 1550, - 1550, 1550, 1555, 1614, 1555, 1614, 1555, 1555, 1614, 1614, - 1614, 1614, 1614, 1555, 1560, 1614, 1560, 1614, 1560, 1560, - 1614, 1614, 1614, 1614, 1614, 1560, 1561, 1561, 1561, 1562, - 1614, 1614, 1614, 1562, 1562, 1562, 1614, 1614, 1614, 1614, - 1562, 1563, 1563, 1563, 1564, 1564, 1564, 1565, 1565, 1565, - 1567, 1567, 1567, 1572, 1572, 1572, 1576, 1576, 1576, 1577, - 1577, 1577, 1578, 1578, 1578, 1579, 1579, 1579, 1580, 1580, - 1580, 1581, 1581, 1581, 1583, 1583, 1583, 1587, 1587, 1587, - 1588, 1614, 1588, 1614, 1588, 1588, 1588, 1614, 1614, 1614, - - 1614, 1588, 1589, 1614, 1589, 1614, 1589, 1589, 1589, 1614, - 1614, 1614, 1614, 1589, 1590, 1590, 1590, 1591, 1591, 1591, - 1595, 1614, 1595, 1614, 1595, 1595, 1595, 1614, 1614, 1614, - 1614, 1595, 1596, 1614, 1596, 1614, 1596, 1596, 1596, 1614, - 1614, 1614, 1614, 1596, 1597, 1614, 1597, 1614, 1597, 1597, - 1597, 1614, 1614, 1614, 1614, 1597, 1598, 1614, 1598, 1614, - 1598, 1598, 1598, 1614, 1614, 1614, 1614, 1598, 1599, 1599, - 1599, 1600, 1614, 1600, 1614, 1600, 1600, 1600, 1614, 1614, - 1614, 1614, 1600, 1601, 1614, 1601, 1614, 1601, 1601, 1601, - 1614, 1614, 1614, 1614, 1601, 1602, 1614, 1602, 1614, 1602, - - 1602, 1602, 1614, 1614, 1614, 1614, 1602, 1603, 1614, 1603, - 1614, 1603, 1603, 1603, 1614, 1614, 1614, 1614, 1603, 1604, - 1614, 1604, 1614, 1604, 1604, 1604, 1614, 1614, 1614, 1614, - 1604, 1605, 1614, 1605, 1614, 1605, 1605, 1605, 1614, 1614, - 1614, 1614, 1605, 1606, 1614, 1606, 1614, 1606, 1606, 1606, - 1614, 1614, 1614, 1614, 1606, 1607, 1614, 1607, 1614, 1607, - 1607, 1607, 1614, 1614, 1614, 1614, 1607, 1608, 1614, 1608, - 1614, 1608, 1608, 1608, 1614, 1614, 1614, 1614, 1608, 1609, - 1614, 1609, 1614, 1609, 1609, 1609, 1614, 1614, 1614, 1614, - 1609, 1610, 1614, 1610, 1614, 1610, 1610, 1610, 1614, 1614, - - 1614, 1614, 1610, 1611, 1614, 1611, 1614, 1611, 1611, 1611, - 1614, 1614, 1614, 1614, 1611, 1612, 1614, 1612, 1614, 1612, - 1612, 1612, 1614, 1614, 1614, 1614, 1612, 1613, 1614, 1613, - 1614, 1613, 1613, 1613, 1614, 1614, 1614, 1614, 1613, 3, + 1517, 1517, 1517, 1518, 1518, 1518, 1522, 1614, 1522, 1614, + 1522, 1522, 1614, 1614, 1614, 1614, 1614, 1522, 1523, 1523, + 1523, 1527, 1614, 1527, 1614, 1527, 1527, 1614, 1614, 1614, + 1614, 1614, 1527, 1532, 1532, 1532, 1537, 1537, 1537, 1538, + 1614, 1614, 1614, 1538, 1538, 1538, 1614, 1614, 1614, 1614, + 1538, 1539, 1539, 1539, 1540, 1614, 1614, 1614, 1540, 1540, + + 1540, 1614, 1614, 1614, 1614, 1540, 1541, 1541, 1541, 1542, + 1542, 1542, 1546, 1546, 1546, 1550, 1550, 1550, 1551, 1551, + 1551, 1555, 1614, 1555, 1614, 1555, 1555, 1614, 1614, 1614, + 1614, 1614, 1555, 1556, 1556, 1556, 1560, 1614, 1560, 1614, + 1560, 1560, 1614, 1614, 1614, 1614, 1614, 1560, 1561, 1561, + 1561, 1562, 1614, 1614, 1614, 1562, 1562, 1562, 1614, 1614, + 1614, 1614, 1562, 1563, 1563, 1563, 1564, 1564, 1564, 1565, + 1565, 1565, 1567, 1567, 1567, 1572, 1572, 1572, 1576, 1576, + 1576, 1577, 1577, 1577, 1578, 1578, 1578, 1579, 1579, 1579, + 1580, 1580, 1580, 1581, 1581, 1581, 1583, 1583, 1583, 1587, + + 1587, 1587, 1588, 1614, 1588, 1614, 1588, 1588, 1588, 1614, + 1614, 1614, 1614, 1588, 1589, 1614, 1589, 1614, 1589, 1589, + 1589, 1614, 1614, 1614, 1614, 1589, 1590, 1590, 1590, 1591, + 1591, 1591, 1595, 1614, 1595, 1614, 1595, 1595, 1595, 1614, + 1614, 1614, 1614, 1595, 1596, 1614, 1596, 1614, 1596, 1596, + 1596, 1614, 1614, 1614, 1614, 1596, 1597, 1614, 1597, 1614, + 1597, 1597, 1597, 1614, 1614, 1614, 1614, 1597, 1598, 1614, + 1598, 1614, 1598, 1598, 1598, 1614, 1614, 1614, 1614, 1598, + 1599, 1599, 1599, 1600, 1614, 1600, 1614, 1600, 1600, 1600, + 1614, 1614, 1614, 1614, 1600, 1601, 1614, 1601, 1614, 1601, + + 1601, 1601, 1614, 1614, 1614, 1614, 1601, 1602, 1614, 1602, + 1614, 1602, 1602, 1602, 1614, 1614, 1614, 1614, 1602, 1603, + 1614, 1603, 1614, 1603, 1603, 1603, 1614, 1614, 1614, 1614, + 1603, 1604, 1614, 1604, 1614, 1604, 1604, 1604, 1614, 1614, + 1614, 1614, 1604, 1605, 1614, 1605, 1614, 1605, 1605, 1605, + 1614, 1614, 1614, 1614, 1605, 1606, 1614, 1606, 1614, 1606, + 1606, 1606, 1614, 1614, 1614, 1614, 1606, 1607, 1614, 1607, + 1614, 1607, 1607, 1607, 1614, 1614, 1614, 1614, 1607, 1608, + 1614, 1608, 1614, 1608, 1608, 1608, 1614, 1614, 1614, 1614, + 1608, 1609, 1614, 1609, 1614, 1609, 1609, 1609, 1614, 1614, + + 1614, 1614, 1609, 1610, 1614, 1610, 1614, 1610, 1610, 1610, + 1614, 1614, 1614, 1614, 1610, 1611, 1614, 1611, 1614, 1611, + 1611, 1611, 1614, 1614, 1614, 1614, 1611, 1612, 1614, 1612, + 1614, 1612, 1612, 1612, 1614, 1614, 1614, 1614, 1612, 1613, + 1614, 1613, 1614, 1613, 1613, 1613, 1614, 1614, 1614, 1614, + 1613, 3, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, - 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, - 1614 + 1614, 1614, 1614 + } ; -static const flex_int16_t yy_chk[16782] = +static const flex_int16_t yy_chk[16794] = { 0, 0, 0, 1, 0, 15, 17, 43, 0, 43, 43, 53, 53, 53, 70, 70, 70, 70, 39, 39, 48, @@ -3593,19 +3595,19 @@ static const flex_int16_t yy_chk[16782] = 1523, 1525, 1525, 1525, 1525, 1525, 1525, 1532, 1538, 1532, 1532, 1534, 1534, 1534, 1534, 1534, 1537, 1540, 1537, 1537, - 1546, 0, 1546, 1546, 1546, 2572, 2572, 1532, 1538, 1550, - 0, 1550, 1550, 1550, 2573, 2573, 1537, 1540, 2575, 2575, + 1546, 0, 1546, 1546, 1546, 2573, 2573, 1532, 1538, 1550, + 0, 1550, 1550, 1550, 2576, 2576, 1537, 1540, 2588, 2588, 1546, 1551, 1551, 1551, 1551, 1551, 1551, 0, 1562, 1550, 1553, 1553, 1553, 1553, 1553, 1553, 1556, 1556, 1556, 1556, 1556, 1556, 1558, 1558, 1558, 1558, 1558, 1558, 1562, 1565, 1567, 1572, 1577, 1572, 1572, 1572, 1576, 1581, 1576, 1576, 1576, 1579, 1583, 1579, 1579, 1580, 1585, 1580, 1580, 1565, - 1567, 1572, 1577, 1586, 1586, 1586, 1576, 1581, 1587, 2576, - 2576, 1579, 1583, 2587, 2587, 1580, 1585, 1588, 1586, 1588, + 1567, 1572, 1577, 1586, 1586, 1586, 1576, 1581, 1587, 2591, + 2591, 1579, 1583, 0, 0, 1580, 1585, 1588, 1586, 1588, - 1588, 1589, 1594, 1589, 1589, 2588, 2588, 1590, 1587, 1590, + 1588, 1589, 1594, 1589, 1589, 0, 0, 1590, 1587, 1590, 1590, 1591, 0, 1591, 1591, 0, 1595, 1588, 1595, 1595, - 0, 1589, 1594, 1596, 0, 1596, 1596, 1590, 2590, 2590, + 0, 1589, 1594, 1596, 0, 1596, 1596, 1590, 0, 0, 1597, 1591, 1597, 1597, 0, 1598, 1595, 1598, 1598, 1600, 0, 1600, 1600, 1596, 1601, 0, 1601, 1601, 0, 1602, 1597, 1602, 1602, 0, 1603, 1598, 1603, 1603, 1604, 1600, @@ -3614,7 +3616,7 @@ static const flex_int16_t yy_chk[16782] = 1608, 0, 1609, 1605, 1609, 1609, 0, 1610, 1606, 1610, 1610, 0, 1611, 1607, 1611, 1611, 1612, 1608, 1612, 1612, - 0, 1613, 1609, 1613, 1613, 2591, 2591, 1610, 0, 0, + 0, 1613, 1609, 1613, 1613, 0, 0, 1610, 0, 0, 0, 0, 1611, 0, 0, 0, 1612, 0, 0, 0, 0, 1613, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, @@ -4806,55 +4808,57 @@ static const flex_int16_t yy_chk[16782] = 2565, 0, 0, 0, 0, 2565, 2566, 2566, 2566, 2567, 0, 0, 0, 2567, 2567, 2567, 0, 0, 0, 0, 2567, 2568, 2568, 2568, 2569, 2569, 2569, 2570, 2570, 2570, - 2571, 2571, 2571, 2574, 0, 2574, 0, 2574, 2574, 0, - 0, 0, 0, 0, 2574, 2577, 0, 2577, 0, 2577, - 2577, 0, 0, 0, 0, 0, 2577, 2578, 2578, 2578, - 2579, 2579, 2579, 2580, 0, 0, 0, 2580, 2580, 2580, - 0, 0, 0, 0, 2580, 2581, 2581, 2581, 2582, 0, - 0, 0, 2582, 2582, 2582, 0, 0, 0, 0, 2582, - - 2583, 2583, 2583, 2584, 2584, 2584, 2585, 2585, 2585, 2586, - 2586, 2586, 2589, 0, 2589, 0, 2589, 2589, 0, 0, - 0, 0, 0, 2589, 2592, 0, 2592, 0, 2592, 2592, - 0, 0, 0, 0, 0, 2592, 2593, 2593, 2593, 2594, - 0, 0, 0, 2594, 2594, 2594, 0, 0, 0, 0, - 2594, 2595, 2595, 2595, 2596, 2596, 2596, 2597, 2597, 2597, - 2598, 2598, 2598, 2599, 2599, 2599, 2600, 2600, 2600, 2601, - 2601, 2601, 2602, 2602, 2602, 2603, 2603, 2603, 2604, 2604, - 2604, 2605, 2605, 2605, 2606, 2606, 2606, 2607, 2607, 2607, - 2608, 0, 2608, 0, 2608, 2608, 2608, 0, 0, 0, - - 0, 2608, 2609, 0, 2609, 0, 2609, 2609, 2609, 0, - 0, 0, 0, 2609, 2610, 2610, 2610, 2611, 2611, 2611, - 2612, 0, 2612, 0, 2612, 2612, 2612, 0, 0, 0, - 0, 2612, 2613, 0, 2613, 0, 2613, 2613, 2613, 0, - 0, 0, 0, 2613, 2614, 0, 2614, 0, 2614, 2614, - 2614, 0, 0, 0, 0, 2614, 2615, 0, 2615, 0, - 2615, 2615, 2615, 0, 0, 0, 0, 2615, 2616, 2616, - 2616, 2617, 0, 2617, 0, 2617, 2617, 2617, 0, 0, - 0, 0, 2617, 2618, 0, 2618, 0, 2618, 2618, 2618, - 0, 0, 0, 0, 2618, 2619, 0, 2619, 0, 2619, - - 2619, 2619, 0, 0, 0, 0, 2619, 2620, 0, 2620, - 0, 2620, 2620, 2620, 0, 0, 0, 0, 2620, 2621, - 0, 2621, 0, 2621, 2621, 2621, 0, 0, 0, 0, - 2621, 2622, 0, 2622, 0, 2622, 2622, 2622, 0, 0, - 0, 0, 2622, 2623, 0, 2623, 0, 2623, 2623, 2623, - 0, 0, 0, 0, 2623, 2624, 0, 2624, 0, 2624, - 2624, 2624, 0, 0, 0, 0, 2624, 2625, 0, 2625, - 0, 2625, 2625, 2625, 0, 0, 0, 0, 2625, 2626, - 0, 2626, 0, 2626, 2626, 2626, 0, 0, 0, 0, - 2626, 2627, 0, 2627, 0, 2627, 2627, 2627, 0, 0, - - 0, 0, 2627, 2628, 0, 2628, 0, 2628, 2628, 2628, - 0, 0, 0, 0, 2628, 2629, 0, 2629, 0, 2629, - 2629, 2629, 0, 0, 0, 0, 2629, 2630, 0, 2630, - 0, 2630, 2630, 2630, 0, 0, 0, 0, 2630, 1614, + 2571, 2571, 2571, 2572, 2572, 2572, 2574, 0, 2574, 0, + 2574, 2574, 0, 0, 0, 0, 0, 2574, 2575, 2575, + 2575, 2577, 0, 2577, 0, 2577, 2577, 0, 0, 0, + 0, 0, 2577, 2578, 2578, 2578, 2579, 2579, 2579, 2580, + 0, 0, 0, 2580, 2580, 2580, 0, 0, 0, 0, + 2580, 2581, 2581, 2581, 2582, 0, 0, 0, 2582, 2582, + + 2582, 0, 0, 0, 0, 2582, 2583, 2583, 2583, 2584, + 2584, 2584, 2585, 2585, 2585, 2586, 2586, 2586, 2587, 2587, + 2587, 2589, 0, 2589, 0, 2589, 2589, 0, 0, 0, + 0, 0, 2589, 2590, 2590, 2590, 2592, 0, 2592, 0, + 2592, 2592, 0, 0, 0, 0, 0, 2592, 2593, 2593, + 2593, 2594, 0, 0, 0, 2594, 2594, 2594, 0, 0, + 0, 0, 2594, 2595, 2595, 2595, 2596, 2596, 2596, 2597, + 2597, 2597, 2598, 2598, 2598, 2599, 2599, 2599, 2600, 2600, + 2600, 2601, 2601, 2601, 2602, 2602, 2602, 2603, 2603, 2603, + 2604, 2604, 2604, 2605, 2605, 2605, 2606, 2606, 2606, 2607, + + 2607, 2607, 2608, 0, 2608, 0, 2608, 2608, 2608, 0, + 0, 0, 0, 2608, 2609, 0, 2609, 0, 2609, 2609, + 2609, 0, 0, 0, 0, 2609, 2610, 2610, 2610, 2611, + 2611, 2611, 2612, 0, 2612, 0, 2612, 2612, 2612, 0, + 0, 0, 0, 2612, 2613, 0, 2613, 0, 2613, 2613, + 2613, 0, 0, 0, 0, 2613, 2614, 0, 2614, 0, + 2614, 2614, 2614, 0, 0, 0, 0, 2614, 2615, 0, + 2615, 0, 2615, 2615, 2615, 0, 0, 0, 0, 2615, + 2616, 2616, 2616, 2617, 0, 2617, 0, 2617, 2617, 2617, + 0, 0, 0, 0, 2617, 2618, 0, 2618, 0, 2618, + + 2618, 2618, 0, 0, 0, 0, 2618, 2619, 0, 2619, + 0, 2619, 2619, 2619, 0, 0, 0, 0, 2619, 2620, + 0, 2620, 0, 2620, 2620, 2620, 0, 0, 0, 0, + 2620, 2621, 0, 2621, 0, 2621, 2621, 2621, 0, 0, + 0, 0, 2621, 2622, 0, 2622, 0, 2622, 2622, 2622, + 0, 0, 0, 0, 2622, 2623, 0, 2623, 0, 2623, + 2623, 2623, 0, 0, 0, 0, 2623, 2624, 0, 2624, + 0, 2624, 2624, 2624, 0, 0, 0, 0, 2624, 2625, + 0, 2625, 0, 2625, 2625, 2625, 0, 0, 0, 0, + 2625, 2626, 0, 2626, 0, 2626, 2626, 2626, 0, 0, + + 0, 0, 2626, 2627, 0, 2627, 0, 2627, 2627, 2627, + 0, 0, 0, 0, 2627, 2628, 0, 2628, 0, 2628, + 2628, 2628, 0, 0, 0, 0, 2628, 2629, 0, 2629, + 0, 2629, 2629, 2629, 0, 0, 0, 0, 2629, 2630, + 0, 2630, 0, 2630, 2630, 2630, 0, 0, 0, 0, + 2630, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, - 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, - 1614 + 1614, 1614, 1614 + } ; /* The intent behind this definition is that it'll catch @@ -4867,7 +4871,7 @@ static const flex_int16_t yy_chk[16782] = #line 1 "vrscanl.l" /* * - * Copyright (C) 1997-2022, OFFIS e.V. + * Copyright (C) 1997-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -4892,7 +4896,7 @@ static const flex_int16_t yy_chk[16782] = #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "vrscani.h" -#line 4896 "vrscanl.c" +#line 4900 "vrscanl.c" /* Make this work on windows (we don't need interactivity anyway) */ /* This seems to be a bug: The .c file won't include it, but .h will * nevertheless. @@ -4900,7 +4904,7 @@ static const flex_int16_t yy_chk[16782] = * Our workaround: Define YY_NO_UNISTD_H in vrscani.h and make * sure that header is included before vrscanl.h. */ -#line 4904 "vrscanl.c" +#line 4908 "vrscanl.c" #define INITIAL 0 @@ -5161,7 +5165,7 @@ YY_DECL #line 101 "vrscanl.l" -#line 5165 "vrscanl.c" +#line 5169 "vrscanl.c" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { @@ -5325,7 +5329,7 @@ YY_RULE_SETUP #line 126 "vrscanl.l" ECHO; YY_BREAK -#line 5329 "vrscanl.c" +#line 5333 "vrscanl.c" case YY_STATE_EOF(INITIAL): yyterminate(); diff --git a/dcmdata/libsrc/vrscanl.l b/dcmdata/libsrc/vrscanl.l index 9d707e24..fa46c792 100644 --- a/dcmdata/libsrc/vrscanl.l +++ b/dcmdata/libsrc/vrscanl.l @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1997-2022, OFFIS e.V. + * Copyright (C) 1997-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -44,7 +44,7 @@ charset_without_control_chars [\040-\133\135-\176\240-\377\033]+ charset_with_control_chars [\040-\176\240-\377\011\012\014\015\033]+ spaces [ ]* zero_byte_padding \000? -dt_offset (\-1200)|(\+1400)|(((\-((0[1-9])|(1[0-1])))|(\+((0[1-9])|(1[0-3])))){tm_minute}) +dt_offset (\-1200)|(\+1400)|(((\-((0[1-9])|(1[0-1])))|(\+((0[0-9])|(1[0-3])))){tm_minute}) da_year (18[5-9][0-9])|(19[0-9]{2})|(20[0-4][0-9]) da_year_dub [0-9]{4} da_month (0[1-9])|(1[0-2]) diff --git a/dcmdata/tests/CMakeLists.txt b/dcmdata/tests/CMakeLists.txt index cae0ef88..98ea562e 100644 --- a/dcmdata/tests/CMakeLists.txt +++ b/dcmdata/tests/CMakeLists.txt @@ -4,6 +4,7 @@ DCMTK_ADD_TEST_EXECUTABLE(dcmdata_tests tchval.cc tdict.cc telemlen.cc + tfrmsiz.cc tests.cc tfilter.cc tgenuid.cc @@ -33,7 +34,7 @@ DCMTK_ADD_TEST_EXECUTABLE(dcmdata_tests ) # make sure executables are linked to the corresponding libraries -DCMTK_TARGET_LINK_MODULES(dcmdata_tests i2d dcmdata oflog ofstd) +DCMTK_TARGET_LINK_MODULES(dcmdata_tests i2d) # This macro parses tests.cc and registers all tests DCMTK_ADD_TESTS(dcmdata) diff --git a/dcmdata/tests/Makefile.dep b/dcmdata/tests/Makefile.dep index 6053caaf..800b37b2 100644 --- a/dcmdata/tests/Makefile.dep +++ b/dcmdata/tests/Makefile.dep @@ -420,6 +420,72 @@ tfilter.o: tfilter.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmdata/dclist.h ../include/dcmtk/dcmdata/dcdatset.h \ ../include/dcmtk/dcmdata/dcitem.h ../include/dcmtk/dcmdata/dcpcache.h \ ../include/dcmtk/dcmdata/dcdict.h ../include/dcmtk/dcmdata/dchashdi.h +tfrmsiz.o: tfrmsiz.cc ../../config/include/dcmtk/config/osconfig.h \ + ../../ofstd/include/dcmtk/ofstd/oftest.h \ + ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ + ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../ofstd/include/dcmtk/ofstd/ofexit.h \ + ../include/dcmtk/dcmdata/dcuid.h ../include/dcmtk/dcmdata/dcdefine.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcitem.h \ + ../include/dcmtk/dcmdata/dctypes.h ../include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../include/dcmtk/dcmdata/dcerror.h ../include/dcmtk/dcmdata/dcxfer.h \ + ../include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dclist.h \ + ../include/dcmtk/dcmdata/dcpcache.h ../include/dcmtk/dcmdata/dcpixseq.h \ + ../include/dcmtk/dcmdata/dcsequen.h ../include/dcmtk/dcmdata/dcelem.h \ + ../include/dcmtk/dcmdata/dcofsetl.h ../include/dcmtk/dcmdata/dcpxitem.h \ + ../include/dcmtk/dcmdata/dcvrobow.h ../include/dcmtk/dcmdata/dcdeftag.h \ + ../include/dcmtk/dcmdata/dcrlerp.h ../include/dcmtk/dcmdata/dcpixel.h \ + ../include/dcmtk/dcmdata/dcvrpobw.h ../include/dcmtk/dcmdata/dcrledrg.h tgenuid.o: tgenuid.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/oftest.h \ ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ diff --git a/dcmdata/tests/Makefile.in b/dcmdata/tests/Makefile.in index c16b46f5..03312127 100644 --- a/dcmdata/tests/Makefile.in +++ b/dcmdata/tests/Makefile.in @@ -26,7 +26,7 @@ LIBDCMXML = -ldcmxml objs = tests.o tpread.o ti2dbmp.o tchval.o tpath.o tvrdatim.o telemlen.o tparser.o \ tdict.o tvrds.o tvrfd.o tvrui.o tvrol.o tvrov.o tvrsv.o tvruv.o tstrval.o \ tspchrs.o tvrpn.o tparent.o tfilter.o tvrcomp.o tmatch.o tnewdcme.o \ - tgenuid.o tsequen.o titem.o ttag.o txfer.o tbytestr.o + tgenuid.o tsequen.o titem.o ttag.o txfer.o tbytestr.o tfrmsiz.o progs = tests diff --git a/dcmdata/tests/tchval.cc b/dcmdata/tests/tchval.cc index e57c1f50..5e07dd6e 100644 --- a/dcmdata/tests/tchval.cc +++ b/dcmdata/tests/tchval.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2009-2016, OFFIS e.V. + * Copyright (C) 2009-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -111,6 +111,17 @@ OFTEST(dcmdata_checkStringValue) CHECK_GOOD( "DT-15", DcmDateTime::checkStringValue("200907", "1") ) CHECK_GOOD( "DT-16", DcmDateTime::checkStringValue("2009", "1") ) CHECK_BAD ( "DT-17", DcmDateTime::checkStringValue("20091", "1") ) + /* check various timezone values, both valid and invalid ones */ + CHECK_GOOD( "DT-18", DcmDateTime::checkStringValue("202501011200+0000")); + CHECK_BAD ( "DT-19", DcmDateTime::checkStringValue("202501011200-0000")); + CHECK_GOOD( "DT-20", DcmDateTime::checkStringValue("202501011200+0100")); + CHECK_GOOD( "DT-21", DcmDateTime::checkStringValue("202412311200-1130")); + CHECK_GOOD( "DT-22", DcmDateTime::checkStringValue("202412311200-1200")); + CHECK_BAD ( "DT-23", DcmDateTime::checkStringValue("202412311200-1230")); + CHECK_GOOD( "DT-24", DcmDateTime::checkStringValue("202412311200+1200")); + CHECK_GOOD( "DT-25", DcmDateTime::checkStringValue("202412311200+1330")); + CHECK_GOOD( "DT-26", DcmDateTime::checkStringValue("202412311200+1400")); + CHECK_BAD ( "DT-27", DcmDateTime::checkStringValue("202412311200+1430")); /* test "Decimal String" */ CHECK_GOOD( "DS-01", DcmDecimalString::checkStringValue(" 0", "1") ) @@ -154,8 +165,9 @@ OFTEST(dcmdata_checkStringValue) // maximum length cannot be checked if given in characters (and not bytes) // CHECK_BAD ( "LO-07", DcmLongString::checkStringValueu("OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany, http://www.offis.de/", "1") ) CHECK_GOOD( "LO-08", DcmLongString::checkStringValue("\\ _2_ \\ _3_ \\ _4_ \\ _5_ \\", "6") ) - CHECK_GOOD( "LO-09", DcmLongString::checkStringValue("ESC\033aping", "1") ) - CHECK_BAD ( "LO-10", DcmLongString::checkStringValue("not allowed: \r\014", "1") ) + // actually, the following test should fail + CHECK_GOOD( "LO-09", DcmLongString::checkStringValue("ESC only allowed for ISO 2022 character set control sequences: \033", "1") ) + CHECK_BAD ( "LO-10", DcmLongString::checkStringValue("also not allowed: \r\014", "1") ) /* test "Long Text" */ CHECK_GOOD( "LT-01", DcmLongText::checkStringValue(" Hello \\ 12345 \\ \344\366\374\337 ", "ISO_IR 100") ) @@ -203,8 +215,10 @@ OFTEST(dcmdata_checkStringValue) CHECK_GOOD( "SH-08", DcmShortString::checkStringValue("\\ _2_ \\ _3_ \\ _4_ \\ _5_ \\", "6") ) CHECK_BAD ( "SH-09", DcmShortString::checkStringValue(" ", "2") ) CHECK_GOOD( "SH-10", DcmShortString::checkStringValue("", "2") ) - CHECK_GOOD( "SH-11", DcmShortString::checkStringValue("ESC\033aping", "1") ) - CHECK_BAD ( "SH-12", DcmShortString::checkStringValue("not allowed: \n\010\r\014", "1") ) + // actually, the following test should fail + CHECK_GOOD( "SH-11", DcmShortString::checkStringValue("not allowed: \033", "1") ) + CHECK_BAD ( "SH-12", DcmShortString::checkStringValue("not allowed: \n\r", "1") ) + CHECK_BAD ( "SH-13", DcmShortString::checkStringValue("not allowed: \010\014", "1") ) /* test "Short Text" */ CHECK_GOOD( "ST-01", DcmShortText::checkStringValue(" umlaut characters are allowed: \304\326\334\344\366\374\naccented characters also: \341\340\351\350\355\354\342\352\364\rand control characters, of course, including \033=ESC ", "ISO_IR 100") ) diff --git a/dcmdata/tests/tests.cc b/dcmdata/tests/tests.cc index 1a14365a..c8b40d42 100644 --- a/dcmdata/tests/tests.cc +++ b/dcmdata/tests/tests.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2011-2024 OFFIS e.V. + * Copyright (C) 2011-2025 OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -126,4 +126,6 @@ OFTEST_REGISTER(dcmdata_xferLookup_2); OFTEST_REGISTER(dcmdata_xferLookup_3); OFTEST_REGISTER(dcmdata_xferLookup_4); OFTEST_REGISTER(dcmdata_putOFStringAtPos); +OFTEST_REGISTER(dcmdata_uncompressedFrameSize); + OFTEST_MAIN("dcmdata") diff --git a/dcmdata/tests/tfilter.cc b/dcmdata/tests/tfilter.cc index b1cbbea4..cd60ec84 100644 --- a/dcmdata/tests/tfilter.cc +++ b/dcmdata/tests/tfilter.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2012-2020, OFFIS e.V. + * Copyright (C) 2012-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -49,7 +49,6 @@ OFTEST( dcmdata_attribute_filter ) // test anonymous filter object OFCHECK( DcmAttributeFilter( DCM_SOPClassUID, UID_ComputedRadiographyImageStorage )( item ) ); -#if !defined(_MSC_VER) || _MSC_VER > 1200 // iterator based filters not supported for VS <= 6.0. // test iterator (array) based range const char* filter_range[3] = { @@ -80,7 +79,5 @@ OFTEST( dcmdata_attribute_filter ) OFCHECK( ct_mr_filter( item ) ); item.putAndInsertString( DCM_SOPClassUID, UID_EnhancedCTImageStorage ); OFCHECK( !cr_ct_mr_filter( item ) ); -#endif - OFCHECK( DcmAttributeFilter()( item ) ); // test default constructed (allow any) filter. } diff --git a/dcmdata/tests/tfrmsiz.cc b/dcmdata/tests/tfrmsiz.cc new file mode 100644 index 00000000..10b0329f --- /dev/null +++ b/dcmdata/tests/tfrmsiz.cc @@ -0,0 +1,138 @@ +/* + * + * Copyright (C) 2024-2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation are maintained by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmdata + * + * Author: Marco Eichelberg + * + * Purpose: test program for DcmElement::getUncompressedFrameSize() + * + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ + +#include "dcmtk/ofstd/oftest.h" +#include "dcmtk/dcmdata/dcdatset.h" +#include "dcmtk/dcmdata/dcpixseq.h" +#include "dcmtk/dcmdata/dcpxitem.h" +#include "dcmtk/dcmdata/dcdeftag.h" +#include "dcmtk/dcmdata/dcrlerp.h" +#include "dcmtk/dcmdata/dcrledrg.h" + +OFTEST(dcmdata_uncompressedFrameSize) +{ + // prepare an uncompressed dataset with the attributes we need for this test + DcmDataset dset; + OFCHECK(dset.putAndInsertUint16(DCM_BitsAllocated, 8).good()); + OFCHECK(dset.putAndInsertUint16(DCM_BitsStored, 8).good()); + OFCHECK(dset.putAndInsertUint16(DCM_HighBit, 7).good()); + OFCHECK(dset.putAndInsertUint16(DCM_Columns, 256).good()); + OFCHECK(dset.putAndInsertUint16(DCM_Rows, 256).good()); + OFCHECK(dset.putAndInsertUint16(DCM_SamplesPerPixel, 3).good()); + OFCHECK(dset.putAndInsertString(DCM_PhotometricInterpretation, "RGB").good()); + + // create and insert an empty pixel data element + DcmPixelData *px = NULL; + OFCHECK(NULL != (px = new DcmPixelData(DCM_PixelData))); + if (px) + { + OFCHECK(dset.insert(px).good()); + + // Tests for DcmElement::decodedBitsAllocated() with uncompressed pixel data. + // This should always return the first parameter (BitsAllocated), unless the + // second parameter (BitsStored) is larger than the first one, in which case + // the result should be zero. + OFCHECK(0 == px->decodedBitsAllocated(8, 9)); + OFCHECK(5 == px->decodedBitsAllocated(5, 1)); + OFCHECK(7 == px->decodedBitsAllocated(7, 1)); + OFCHECK(8 == px->decodedBitsAllocated(8, 8)); + OFCHECK(9 == px->decodedBitsAllocated(9, 8)); + OFCHECK(13 == px->decodedBitsAllocated(13, 8)); + OFCHECK(23 == px->decodedBitsAllocated(23, 8)); + OFCHECK(255 == px->decodedBitsAllocated(255, 8)); + + // Tests for getUncompressedFrameSize() with an uncompressed dataset + Uint32 frameSize = 0; + OFCHECK(px->getUncompressedFrameSize(&dset, frameSize, OFTrue).good()); + OFCHECK(196608 == frameSize); + + OFCHECK(dset.putAndInsertUint16(DCM_BitsAllocated, 12).good()); + OFCHECK(dset.putAndInsertUint16(DCM_BitsStored, 12).good()); + OFCHECK(px->getUncompressedFrameSize(&dset, frameSize, OFTrue).good()); + OFCHECK(294912 == frameSize); + + OFCHECK(dset.putAndInsertUint16(DCM_BitsAllocated, 16).good()); + OFCHECK(dset.putAndInsertUint16(DCM_BitsStored, 12).good()); + OFCHECK(px->getUncompressedFrameSize(&dset, frameSize, OFTrue).good()); + OFCHECK(393216 == frameSize); + + OFCHECK(dset.putAndInsertUint16(DCM_BitsAllocated, 32).good()); + OFCHECK(dset.putAndInsertUint16(DCM_BitsStored, 32).good()); + OFCHECK(px->getUncompressedFrameSize(&dset, frameSize, OFTrue).good()); + OFCHECK(786432 == frameSize); + + // set color model to YBR_FULL_422 and create a frame size indicating that we have in fact RGB + Uint16 *pxdata = NULL; + OFCHECK(dset.putAndInsertUint16(DCM_BitsAllocated, 8).good()); + OFCHECK(dset.putAndInsertUint16(DCM_BitsStored, 8).good()); + OFCHECK(dset.putAndInsertString(DCM_PhotometricInterpretation, "YBR_FULL_422").good()); + OFCHECK(px->createUint16Array(98304, pxdata).good()); + OFCHECK(px->getUncompressedFrameSize(&dset, frameSize, OFTrue).good()); + OFCHECK(196608 == frameSize); + + // set color model to YBR_FULL_422 and create a frame size indicating that we have in fact uncompressed YBR_FULL_422 + OFCHECK(px->createUint16Array(65536, pxdata).good()); + OFCHECK(px->getUncompressedFrameSize(&dset, frameSize, OFTrue).good()); + OFCHECK(131072 == frameSize); + + // now convert the dataset to an RLE compressed one and check again + OFCHECK(dset.putAndInsertString(DCM_PhotometricInterpretation, "RGB").good()); + DcmRLERepresentationParameter rle_rp; + DcmPixelSequence *pixelSequence = new DcmPixelSequence(DCM_PixelSequenceTag); + DcmPixelItem *offsetTable = new DcmPixelItem(DCM_PixelItemTag); + OFCHECK((NULL != pixelSequence) && (NULL != offsetTable)); + if (pixelSequence && offsetTable) + { + pixelSequence->insert(offsetTable); + px->putOriginalRepresentation(EXS_RLELossless, &rle_rp, pixelSequence); + + // this should fail because the RLE decoder is not registered yet, + // and, therefore, the computation of the uncompressed frame size will fail + OFCHECK(EC_InvalidValue == px->getUncompressedFrameSize(&dset, frameSize, OFTrue)); + + // register RLE decompression codec + DcmRLEDecoderRegistration::registerCodecs(); + + // now we can compute the frame size for the RLE compressed image + OFCHECK(px->getUncompressedFrameSize(&dset, frameSize, OFTrue).good()); + OFCHECK(196608 == frameSize); + + // the RLE codec should refuse the size calculation if BitsAllocated is not a multiple of 8 + OFCHECK(dset.putAndInsertUint16(DCM_BitsAllocated, 12).good()); + OFCHECK(dset.putAndInsertUint16(DCM_BitsStored, 12).good()); + OFCHECK(EC_InvalidValue == px->getUncompressedFrameSize(&dset, frameSize, OFTrue)); + + // these calls should now invoke the routine in the RLE codec that computes + // decoded bits allocated. + OFCHECK(0 == px->decodedBitsAllocated(8, 9)); + OFCHECK(0 == px->decodedBitsAllocated(5, 1)); + OFCHECK(8 == px->decodedBitsAllocated(8, 8)); + OFCHECK(0 == px->decodedBitsAllocated(13, 8)); + OFCHECK(16 == px->decodedBitsAllocated(16, 8)); + OFCHECK(64 == px->decodedBitsAllocated(64, 8)); + + // avoid memory leak + DcmRLEDecoderRegistration::cleanup(); + } + } +} diff --git a/dcmdata/tests/tsequen.cc b/dcmdata/tests/tsequen.cc index 3a6959ba..c8d2b9e8 100644 --- a/dcmdata/tests/tsequen.cc +++ b/dcmdata/tests/tsequen.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019-2022, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2019-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -40,19 +40,29 @@ OFTEST(dcmdata_sequenceInsert) DcmSequenceOfItems sequence(DCM_OtherPatientIDsSequence); /* add a large number of items to the sequence */ unsigned long counter = 0; + DcmItem *items[NUMBER_OF_ITEMS]; for (unsigned long i = 0; i < NUMBER_OF_ITEMS; ++i) { - if (sequence.insert(new DcmItem()).good()) + items[i] = new DcmItem(); + if (sequence.insert(items[i]).good()) ++counter; } /* check whether that worked (for-loop shouldn't take too long) */ OFCHECK_EQUAL(counter, NUMBER_OF_ITEMS); /* access specific items (performance should be no issue) */ - OFCHECK(sequence.getItem(0) != NULL); - OFCHECK(sequence.getItem(2) != NULL); + OFCHECK(sequence.getItem(0) == items[0]); + OFCHECK(sequence.getItem(2) == items[2]); OFCHECK(sequence.getItem(NUMBER_OF_ITEMS) == NULL); - OFCHECK(sequence.getItem(NUMBER_OF_ITEMS - 1) != NULL); - OFCHECK(sequence.getItem(NUMBER_OF_ITEMS - 2) != NULL); + OFCHECK(sequence.getItem(NUMBER_OF_ITEMS - 1) == items[NUMBER_OF_ITEMS - 1]); + OFCHECK(sequence.getItem(NUMBER_OF_ITEMS - 2) == items[NUMBER_OF_ITEMS - 2]); + /* insert a single item before the current item */ + DcmItem *newItem = new DcmItem(); + OFCHECK(sequence.insertAtCurrentPos(newItem, OFTrue /*before*/).good()); + OFCHECK(sequence.getItem(NUMBER_OF_ITEMS - 2) == newItem); + /* the items after the new item should have been shifted by one */ + OFCHECK(sequence.getItem(NUMBER_OF_ITEMS - 1) == items[NUMBER_OF_ITEMS - 2]); + OFCHECK(sequence.getItem(NUMBER_OF_ITEMS) == items[NUMBER_OF_ITEMS - 1]); + OFCHECK(sequence.getItem(NUMBER_OF_ITEMS + 1) == NULL); } diff --git a/dcmdata/tests/tvrdatim.cc b/dcmdata/tests/tvrdatim.cc index 2f90bf5f..db00f1b9 100644 --- a/dcmdata/tests/tvrdatim.cc +++ b/dcmdata/tests/tvrdatim.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2020, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by diff --git a/dcmect/include/dcmtk/dcmect/enhanced_ct.h b/dcmect/include/dcmtk/dcmect/enhanced_ct.h index 3a4c39fa..9745fad1 100644 --- a/dcmect/include/dcmtk/dcmect/enhanced_ct.h +++ b/dcmect/include/dcmtk/dcmect/enhanced_ct.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019-2024, Open Connections GmbH + * Copyright (C) 2019-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -204,6 +204,16 @@ public: */ virtual OFBool getCheckFGOnWrite(); + /** Set whether attribute values should be checked on writing, i.e. if writing + * should fail if attribute values violate their VR, VM, character set or value length. + * A missing but required value is always considered an error, independent of this setting. + * If set to OFFalse, writing will always succeed, even if attribute value constraints + * are violated. A warning instead of an error will be printed to the logger. + * @param doCheck If OFTrue, attribute value errors are handled as errors on writing, if OFFalse + * any errors are ignored. + */ + virtual void setValueCheckOnWrite(const OFBool doCheck); + // -------------------- creation --------------------- /** Factory method to create an Enhanced CT object from the minimal @@ -739,7 +749,7 @@ private: IODCommonInstanceReferenceModule m_CommonInstanceReferenceModule; /// Binary frame data - OFVector m_Frames; + OFVector m_Frames; /// Multi-frame Functional Groups high level interface FGInterface m_FGInterface; diff --git a/dcmect/libsrc/Makefile.dep b/dcmect/libsrc/Makefile.dep index 7f2cabea..3ff3d9eb 100644 --- a/dcmect/libsrc/Makefile.dep +++ b/dcmect/libsrc/Makefile.dep @@ -70,6 +70,7 @@ enhanced_ct.o: enhanced_ct.cc \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../dcmiod/include/dcmtk/dcmiod/modcommoninstanceref.h \ ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ diff --git a/dcmect/libsrc/enhanced_ct.cc b/dcmect/libsrc/enhanced_ct.cc index 46e0aa68..527cc106 100644 --- a/dcmect/libsrc/enhanced_ct.cc +++ b/dcmect/libsrc/enhanced_ct.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019-2024, Open Connections GmbH + * Copyright (C) 2019-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -110,7 +110,7 @@ struct EctEnhancedCT::WriteVisitor return FG_EC_PixelDataTooLarge; } const size_t numPixelsFrame = OFstatic_cast(size_t, rows) * OFstatic_cast(size_t, cols); - const size_t numBytesFrame = m_CT.m_Frames[0]->length; + const size_t numBytesFrame = m_CT.m_Frames[0]->getLengthInBytes(); if (numBytesFrame != numPixelsFrame * 2) { DCMECT_ERROR("Invalid number of bytes per frame: Expected " << numPixelsFrame * 2 << " but got " @@ -124,16 +124,16 @@ struct EctEnhancedCT::WriteVisitor { pixData->setVR(EVR_OW); Uint16* ptr = NULL; - size_t numBytesTotal = numBytesFrame * numFrames / 2; + size_t numBytesTotal = numBytesFrame * numFrames; if (numBytesTotal <= 4294967294UL) { - result = pixData->createUint16Array(OFstatic_cast(Uint32, numBytesTotal), ptr); + result = pixData->createUint16Array(OFstatic_cast(Uint32, numBytesTotal / 2), ptr); // copy all frames into CT's frame structure if (ptr) { for (size_t f = 0; f < numFrames; ++f) { - memcpy(ptr, m_CT.m_Frames[f]->pixData, numBytesFrame); + memcpy(ptr, m_CT.m_Frames[f]->getPixelData(), numBytesFrame); ptr += numPixelsFrame; } return m_Item.insert(pixData); @@ -160,7 +160,7 @@ struct EctEnhancedCT::WriteVisitorConcatenation // Inner class that implements the writing to Concatentions via ConcatenationCreator class // Constructor, sets parameters the visitor works on in operator() - WriteVisitorConcatenation(EctEnhancedCT& m, Uint8*& pixData, size_t& pixDataLength) + WriteVisitorConcatenation(EctEnhancedCT& m, Uint16*& pixData, size_t& pixDataLength) : m_CT(m) , m_pixData(pixData) , m_pixDataLength(pixDataLength) @@ -186,20 +186,20 @@ struct EctEnhancedCT::WriteVisitorConcatenation m_CT.getRows(rows); m_CT.getColumns(cols); const size_t numFrames = m_CT.m_Frames.size(); - const size_t numBytesFrame = m_CT.m_Frames[0]->length; + const size_t numBytesFrame = m_CT.m_Frames[0]->getLengthInBytes(); // Creates the correct pixel data element, based on the image pixel module used. m_pixDataLength = numBytesFrame * numFrames; - m_pixData = new Uint8[m_pixDataLength]; + m_pixData = new Uint16[m_pixDataLength / 2]; if (m_pixData) { - Uint8* ptr = m_pixData; + Uint16* ptr = m_pixData; // copy all frames into CT's frame structure if (ptr) { for (size_t f = 0; f < numFrames; ++f) { - memcpy(ptr, m_CT.m_Frames[f]->pixData, numBytesFrame); - ptr += numBytesFrame; + memcpy(ptr, m_CT.m_Frames[f]->getPixelData(), numBytesFrame); + ptr += numBytesFrame / 2; } return EC_Normal; } @@ -211,7 +211,7 @@ struct EctEnhancedCT::WriteVisitorConcatenation // Members, i.e. parameters to operator() EctEnhancedCT& m_CT; - Uint8*& m_pixData; + Uint16*& m_pixData; size_t& m_pixDataLength; }; @@ -276,12 +276,10 @@ struct EctEnhancedCT::ReadVisitor { for (Uint32 n = 0; n < numFrames; n++) { - DcmIODTypes::Frame* f = new DcmIODTypes::Frame; + DcmIODTypes::Frame* f = new DcmIODTypes::Frame(numBytesFrame / 2); if (f) { - f->length = numBytesFrame; - f->pixData = new Uint8[f->length]; - memcpy(f->pixData, pixData + n * numBytesFrame / 2, numBytesFrame); + memcpy(f->m_pixData, pixData + n * numBytesFrame / 2, numBytesFrame); m_CT.m_Frames.push_back(f); } else @@ -328,12 +326,10 @@ OFCondition EctEnhancedCT::Frames::addFrame(PixelType* data, { if (!perFrameInformation.empty()) { - OFunique_ptr f(new DcmIODTypes::Frame); + OFunique_ptr > f(new DcmIODTypes::Frame(numPixels)); if (f) { - f->length = numPixels * sizeof(PixelType); - f->pixData = new Uint8[f->length]; - memcpy(f->pixData, data, f->length); + memcpy(f->m_pixData, data, f->getLengthInBytes()); m_CT.m_Frames.push_back(f.release()); OFVector::const_iterator fg = perFrameInformation.begin(); while (result.good() && (fg != perFrameInformation.end())) @@ -361,14 +357,15 @@ PixelType* EctEnhancedCT::Frames::getFrame(const size_t frameNumber) { if (frameNumber < m_CT.m_Frames.size()) { - return (PixelType*)(m_CT.m_Frames[frameNumber]->pixData); + DcmIODTypes::Frame* f = OFstatic_cast(DcmIODTypes::Frame*, m_CT.m_Frames[frameNumber]); + return f->getPixelDataTyped(); } return NULL; } // Helper "class" that returns Frames offering API to the pixel's frame bulk // data by offering the dedicated data type, e.g. Float32 instead of the -// internally stored generic Uint8 array. +// internally stored generic Uint16 array. // struct EctEnhancedCT::GetFramesVisitor { @@ -514,7 +511,7 @@ EctEnhancedCT::loadConcatenation(ConcatenationLoader& cl, const OFString& concat DcmDataset dset; ct = NULL; - OFVector frames; + OFVector frames; OFCondition result = cl.load(concatenationUID, &dset, frames); if (result.good()) { @@ -577,6 +574,17 @@ OFBool EctEnhancedCT::getCheckFGOnWrite() return m_FGInterface.getCheckOnWrite(); } +void EctEnhancedCT::setValueCheckOnWrite(const OFBool doCheck) +{ + m_SynchronizationModule.setValueCheckOnWrite(doCheck); + m_EnhancedGeneralEquipmentModule.setValueCheckOnWrite(doCheck); + m_FG.setValueCheckOnWrite(doCheck); + m_DimensionModule.setValueCheckOnWrite(doCheck); + m_AcquisitionContextModule.setValueCheckOnWrite(doCheck); + m_CommonInstanceReferenceModule.setValueCheckOnWrite(doCheck); + DcmIODImage::setValueCheckOnWrite(doCheck); +} + // ------------------ Creation ----------------------- OFCondition EctEnhancedCT::create(EctEnhancedCT*& ct, @@ -1051,6 +1059,7 @@ OFCondition EctEnhancedCT::setVolumeBasedCalculationTechnique(const OFString& va return result; } + // -------------------- Protected Helpers -------------------------- OFCondition EctEnhancedCT::read(DcmItem& dataset) @@ -1083,7 +1092,7 @@ OFCondition EctEnhancedCT::writeConcatenation(ConcatenationCreator& cc) if (!item) return EC_MemoryExhausted; - Uint8* pixData = NULL; + Uint16* pixData = NULL; size_t pixDataLength = 0; OFCondition result @@ -1190,7 +1199,15 @@ OFCondition EctEnhancedCT::readGeneric(DcmItem& dataset) } IODImage::read(dataset); - m_SynchronizationModule.read(dataset); + if (m_SynchronizationModuleEnabled) + { + // Synchronization Module is type C ("Required if time synchronization was​ applied"), + // so we make it optional for reading to avoid warnings on the attributes, and then reset rules + // to the default state. + m_SynchronizationModule.makeOptional(); + m_SynchronizationModule.read(dataset); + m_SynchronizationModule.resetRules(); + } m_EnhancedGeneralEquipmentModule.read(dataset); m_FG.read(dataset); m_DimensionModule.read(dataset); diff --git a/dcmect/tests/CMakeLists.txt b/dcmect/tests/CMakeLists.txt index 89f73c38..97c69cb4 100644 --- a/dcmect/tests/CMakeLists.txt +++ b/dcmect/tests/CMakeLists.txt @@ -7,7 +7,7 @@ DCMTK_ADD_TEST_EXECUTABLE(dcmect_tests ) # make sure executables are linked to the corresponding libraries -DCMTK_TARGET_LINK_MODULES(dcmect_tests dcmect dcmfg dcmdata oflog ofstd) +DCMTK_TARGET_LINK_MODULES(dcmect_tests dcmect dcmfg) # This macro parses tests.cc and registers all tests DCMTK_ADD_TESTS(dcmect) diff --git a/dcmect/tests/Makefile.dep b/dcmect/tests/Makefile.dep index acafe2c0..7cc0be3c 100644 --- a/dcmect/tests/Makefile.dep +++ b/dcmect/tests/Makefile.dep @@ -86,6 +86,7 @@ t_huge_concat.o: t_huge_concat.cc \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../dcmiod/include/dcmtk/dcmiod/modcommoninstanceref.h \ ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ @@ -219,6 +220,7 @@ t_overflow.o: t_overflow.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../dcmiod/include/dcmtk/dcmiod/modcommoninstanceref.h \ ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ @@ -374,6 +376,7 @@ t_roundtrip.o: t_roundtrip.cc \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../dcmiod/include/dcmtk/dcmiod/modcommoninstanceref.h \ ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ diff --git a/dcmect/tests/t_huge_concat.cc b/dcmect/tests/t_huge_concat.cc index 686552de..d83a4684 100644 --- a/dcmect/tests/t_huge_concat.cc +++ b/dcmect/tests/t_huge_concat.cc @@ -60,7 +60,7 @@ static const Uint16 NUM_ROWS = 4000; // Number of Columns of image, might be changed for testing purposes static const Uint16 NUM_COLS = 4000; // Number of Frames of source image, might be changed for testing purposes -static const Uint16 NUM_FRAMES = 200; +static const Uint16 NUM_FRAMES = 100; // Number of Frames of in concatenation images, should not be changed since the // dumped checked against will assume Number of Frames = 1 static const size_t NUM_FRAMES_CONCAT = 1; @@ -592,6 +592,8 @@ void loadAndCheckConcatenation(const OFList& concats) DcmDataset merged; EctEnhancedCT* mergedCT = NULL; result = EctEnhancedCT::loadConcatenation(cl, cl.getInfo().begin()->first, mergedCT); + DcmItem item; + mergedCT->writeDataset(item); if (result.good()) { ConcatenationCreator cc; diff --git a/dcmect/tests/t_overflow.cc b/dcmect/tests/t_overflow.cc index 4e07a5c5..18dfc67d 100644 --- a/dcmect/tests/t_overflow.cc +++ b/dcmect/tests/t_overflow.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2024, OFFIS e.V. + * Copyright (C) 2024-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -85,8 +85,9 @@ OFTEST(dcmect_overflow) OFTempFile tf(O_RDWR, "", "t_overflow", ".dcm"); OFCondition result; - result = ct->saveFile("output.dcm", EXS_LittleEndianExplicit); + result = ct->saveFile(tf.getFilename(), EXS_LittleEndianExplicit); OFCHECK_MSG(result == ECT_InvalidPixelInfo, result.text()); + delete ct; } static EctEnhancedCT *create() diff --git a/dcmfg/include/dcmtk/dcmfg/concatenationcreator.h b/dcmfg/include/dcmtk/dcmfg/concatenationcreator.h index a247fd5b..c78ff878 100644 --- a/dcmfg/include/dcmtk/dcmfg/concatenationcreator.h +++ b/dcmfg/include/dcmtk/dcmfg/concatenationcreator.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019-2024, Open Connections GmbH + * Copyright (C) 2019-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -27,6 +27,7 @@ #include "dcmtk/ofstd/ofcond.h" #include "dcmtk/ofstd/offile.h" #include "dcmtk/dcmfg/fgdefine.h" +#include "dcmtk/dcmiod/iodtypes.h" class DcmItem; class DcmSequenceOfItems; @@ -89,6 +90,19 @@ public: virtual OFCondition setCfgInput(DcmItem* srcDataset, Uint8* pixelData, size_t pixelDataLength, OFBool transferOwnership); + /** Set input dataset with separate pixel data that should be split + * into a number of concatenation instances. + * @param srcDataset The dataset to read from (must not be NULL and not contain + * the Pixel Data attribute (on main level) + * @param pixelData Raw buffer of source pixel data + * @param pixelDataLength Length of pixelData buffer in bytes + * @param transferOwnership If OFTrue, the ConcatenationCreator class will + * free memory of the srcDataset and pixelData after processing. + * @return EC_Normal if input is considered valid (up to now), error otherwise + */ + virtual OFCondition + setCfgInput(DcmItem* srcDataset, Uint16* pixelData, size_t pixelDataLength, OFBool transferOwnership); + /** Set number of frames that should go into a single concatenation instance produced. * The last concatenation instance might have less frames. This setting also * directly determines the number of instances produced for a specific input. @@ -180,6 +194,15 @@ protected: */ virtual OFCondition configureCommon(); + /** Prepare source pixel data (m_srcPixelData) according to the pixel data + * existing in the source dataset. + * @param srcDataset The source dataset to read from + * @param transferOwnership If OFTrue, this class (m_srcPixelData) + * takes ownership of the pixel data, i.e. frees memory on destruction. + * @return EC_Normal if successful, error otherwise + */ + virtual OFCondition initSrcPixelData(DcmItem* srcDataset, OFBool transferOwnership); + private: /// Maximum number of instances that make up a Concatenation (=2^16-1=65535), @@ -220,7 +243,7 @@ private: /// before the call to this class returns). /// Once the ConcatenationCreator creator class goes out of scope or reset() is being called, /// it is set to NULL. If m_cfgTransferOwnership is OFTrue, memory is freed by this class, too. - Uint8* m_srcPixelData; + DcmIODTypes::FrameBase* m_srcPixelData; /// VR of pixel data extracted/derived from source dataset. EVR_OB and EVR_OW are supported. /// Initially set to EVR_Unknown. diff --git a/dcmfg/include/dcmtk/dcmfg/concatenationloader.h b/dcmfg/include/dcmtk/dcmfg/concatenationloader.h index ecd7f308..a9b3c5a6 100644 --- a/dcmfg/include/dcmtk/dcmfg/concatenationloader.h +++ b/dcmfg/include/dcmtk/dcmfg/concatenationloader.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019-2024, Open Connections GmbH + * Copyright (C) 2019-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -48,6 +48,7 @@ class DcmItem; * and a vector containing all frames of the merged instance. * */ + class DCMTK_DCMFG_EXPORT ConcatenationLoader { @@ -239,7 +240,7 @@ public: * @return EC_Normal if loading Concatenation worked, error otherwise. */ virtual OFCondition - load(const OFString& concatenationUID, DcmDataset* dataset, OFVector& frames); + load(const OFString& concatenationUID, DcmDataset* dataset, OFVector& frames); protected: /** Handles single file of a Concatenation and extracts structure for later @@ -338,6 +339,13 @@ protected: */ virtual OFCondition insertDestinationAttributes(); + /** Compute bytes per frame based on Rows, Columns and Bits Allocated. + * @param rows The number of Rows + * @param cols The number of Columns + * @param bitsAlloc The Bits Allocated value (only 1, 8 or 16 supported) + * @param bytes_per_frame The resulting number of bytes per frame + * @return EC_Normal if successful, error otherwise. + */ virtual OFCondition computeBytesPerFrame(const Uint16 rows, const Uint16 cols, const Uint16 bitsAlloc, size_t& bytes_per_frame); @@ -364,7 +372,7 @@ private: /// produced by the load() method. Once a merged instance is provided to /// the caller, as a result of load(), the caller is responsible for /// deleting the related memory. - OFVector m_Frames; + OFVector m_Frames; }; #endif // CONCATENATIONLOADER_H diff --git a/dcmfg/include/dcmtk/dcmfg/fg.h b/dcmfg/include/dcmtk/dcmfg/fg.h index 3ec37382..54a50b8e 100644 --- a/dcmfg/include/dcmtk/dcmfg/fg.h +++ b/dcmfg/include/dcmtk/dcmfg/fg.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -57,6 +57,11 @@ public: */ virtual void clear(); + /** Get the number of functional groups in the set + * @return Number of functional groups in the set + */ + virtual size_t size() const; + /** Find a functional group by its type * @param fgType The type of the functional group * @return The functional group, if found, NULL otherwise diff --git a/dcmfg/include/dcmtk/dcmfg/fgderimg.h b/dcmfg/include/dcmtk/dcmfg/fgderimg.h index a8c3b139..588cc7c1 100644 --- a/dcmfg/include/dcmtk/dcmfg/fgderimg.h +++ b/dcmfg/include/dcmtk/dcmfg/fgderimg.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -24,7 +24,7 @@ #include "dcmtk/config/osconfig.h" -#include "dcmtk/dcmdata/dcdatset.h" +#include "dcmtk/dcmdata/dcvrcs.h" #include "dcmtk/dcmfg/fgbase.h" #include "dcmtk/dcmiod/iodmacro.h" #include "dcmtk/ofstd/ofstring.h" @@ -66,6 +66,20 @@ public: */ virtual ImageSOPInstanceReferenceMacro& getImageSOPInstanceReference(); + /** Get Spatial Locations Preserved + * @param value Reference to variable in which the value should be stored + * @param pos Index of the value to get (0..vm-1), -1 for all components + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getSpatialLocationsPreserved(OFString& value, const signed long pos = 0) const; + + /** Set Spatial Locations Preserved + * @param value Value to be set (single value only) or "" for no value + * @param checkValue Check 'value' for conformance with VR (CS) and VM (1) if enabled + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition setSpatialLocationsPreserved(const OFString& value, const OFBool checkValue); + /** Reads source image item from given item * @param itemOfSourceImageSequence Reference to item of Source Image Sequence * @param clearOldData If OFTue, old data in this class is cleared before reading @@ -118,6 +132,9 @@ private: /// Contains the referenced images (as represented by one of the items of /// "this" Source Image Sequence) ImageSOPInstanceReferenceMacro m_ImageSOPInstanceReference; + + /// Denotes whether spatial locations from source image have been preseved + DcmCodeString m_SpatialLocationsPreserved; }; /// Iterator for traversing over items of the Source Image Sequence diff --git a/dcmfg/include/dcmtk/dcmfg/fginterface.h b/dcmfg/include/dcmtk/dcmfg/fginterface.h index c046cfc9..00f48a74 100644 --- a/dcmfg/include/dcmtk/dcmfg/fginterface.h +++ b/dcmfg/include/dcmtk/dcmfg/fginterface.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -40,6 +40,7 @@ class DCMTK_DCMFG_EXPORT FGInterface { public: + /// Type representing per-frame functional groups, i.e.\ a number of /// functional groups assigned to each frame typedef OFMap PerFrameGroups; @@ -201,6 +202,20 @@ public: */ virtual OFBool getCheckOnWrite(); + /** Sets the maximum number of threads to be used for reading and writing per-frame functional groups. + * @param numThreads The maximum number of threads to use + * The number of threads will be adjusted to the number of frames, i.e.\ there will + * be no more threads used than one fifth the number of frames (so that each thread must at least handle + * 10 frames, since otherwise the overhead of starting threads would be too high). The + * number is adjusted on the fly. + */ + virtual void setUseThreads(const Uint32 numThreads); + + /** Returns the number of threads to be used for writing per-frame functional groups. + * @return The number of threads to use + */ + virtual Uint32 getUseThreads() const; + protected: /** Get shared functional group based on its type * @param fgType The type of functional group @@ -254,12 +269,16 @@ protected: */ virtual OFCondition readPerFrameFG(DcmItem& dataset); + virtual OFCondition readPerFrameFGParallel(DcmSequenceOfItems& perFrameFGSeq, const Uint32 numThreads); + + virtual OFCondition readPerFrameFGSequential(DcmSequenceOfItems& perFrameFGSeq); + /** Read single functional group into the item provided * @param fgItem The item to read from * @param groups The resulting group after reading * @return EC_Normal if reading was successful, error otherwise */ - virtual OFCondition readSingleFG(DcmItem& fgItem, FunctionalGroups& groups); + static OFCondition readSingleFG(DcmItem& fgItem, FunctionalGroups& groups); /** Write Shared Functional Group Sequence to given item * @param dataset The item to write to @@ -273,9 +292,23 @@ protected: */ virtual OFCondition writePerFrameFG(DcmItem& dataset); + /** Write Per-Frame Functional Group Sequence to given item in parallel + * @param dataset The item to write to + * @param numThreads The maximum number of threads to use + * @return EC_Normal if writing was successful, error otherwise + */ + virtual OFCondition writePerFrameFGParallel(DcmItem& dataset, const Uint32 numThreads); + + /** Write Per-Frame Functional Group Sequence to given item in sequential mode, + * i.e.\ no extra threads are used. + * @param dataset The item to write to + * @return EC_Normal if writing was successful, error otherwise + */ + virtual OFCondition writePerFrameFGSequential(DcmItem& dataset); + /** Convert a shared functional group to a per-frame one by copying the * shared one into a per-frame one for each frame and deleting the shared one - * aftewrards. + * afterwards. * @param fgType The type of functional group to convert * @return EC_Normal if conversion worked out, FG_EC_NoSuchGroup if such a * group does not exist and other error otherwise. In the last case @@ -284,6 +317,150 @@ protected: */ virtual OFCondition convertSharedToPerFrame(const DcmFGTypes::E_FGType fgType); + /** Find an adequate number of threads to use for reading and writing per-frame functional groups. + * The number is adjusted to the number of frames, i.e.\ there will be no more threads used + * than one fifth the number of frames (so that each thread must at least handle + * 5 frames, since otherwise the overhead of starting threads would be too high). + * @param numFrames The number of frames to read/write + * @param userThreadSetting The user-defined number of threads to use + * @return The adjusted number of threads to use + */ + virtual Uint32 findAdequateNumberOfThreads(const Uint32 numFrames, const Uint32 userThreadSetting); + + /// Threaded functional group writer, used to write per-frame functional groups + /// in parallel. Each thread gets assigned some frames and writes the functional groups + /// for those frames to the output vector. + struct ThreadedFGWriter : public OFThread + { + /// Vector of pairs of frame number and functional groups to write for that frame + OFVector >* m_frameGroups; + /// Output vector, where the per-frame items are written to + /// (one item per frame containing all functional groups for that frame). + // All threads write to the same vector, so it must be protected by a mutex. + /// The vector is resized to the total number of frames before starting the threads. + OFVector* m_perFrameResultItems; + /// Mutex to protect the output vector + OFMutex* m_perFrameResultItemsMutex; + /// Start frame this thread should handle (inclusive, starts with 0) + Uint32 m_startFrame; + /// End frame this thread should handle (exclusive, i.e.\ the last frame + /// this thread handles is m_endFrame - 1) + Uint32 m_endFrame; + /// Mutex to protect error output + OFMutex* m_errorMutex; + /// Pointer to a condition variable that is set if an error occurs + /// during writing. This is used to signal the main thread that an error + /// occurred during writing. The main thread can then check the error + /// condition variable to see if an error occurred and handle it accordingly. + OFConditionConst* m_errorOccurred; + + /** Initialize the thread + * @param frameGroups Input vector of pairs of frame number and functional groups to write for that frame + * @param perFrameResultItems Output vector, where the per-frame items are written to, + * (frame number as index, one item per frame containing all functional groups for that frame). + * @param perFrameResultItemsMutex Mutex to protect the output vector + * @param startFrame Start frame this thread should handle (inclusive, starts with 0) + * @param endFrame End frame this thread should handle (exclusive, i.e.\ the last frame this thread handles is m_endFrame - 1) + * @param errorOccurred Pointer to a condition variable that is set if an error occurs + * @param errorMutex Mutex to protect error output + */ + void init(OFVector >* frameGroups, + OFVector* perFrameResultItems, + OFMutex* perFrameResultItemsMutex, + const Uint32 startFrame, + const Uint32 endFrame, + OFConditionConst* errorOccurred, + OFMutex* errorMutex); + + /// Default constructor + ThreadedFGWriter(); + + /// Destructor, nothing to do + ~ThreadedFGWriter(); + + /** Run method, called by OFThread::start() + * This method will write the functional groups for the frames assigned + * to this thread to the output vector. It will stop in case of an error + * and set the error condition variable to indicate that an error occurred. + */ + void run(); + }; + + /// Threaded functional group reader, used to read per-frame functional groups + /// in parallel. Each thread gets assigned some frames and reads the functional groups + /// for those frames from the input vector. + /// The results are stored in the output vector, which is protected by a mutex. + struct ThreadedFGReader : public OFThread + { + /// Input vector of per-frame items, one item per frame + /// containing all functional groups for that frame. + OFVector* m_perFrameItems; + /// Mutex to protect the input vector + OFMutex* m_perFrameItemsMutex; + /// Output vector of per-frame functional groups, one item per frame + /// containing all functional groups for that frame. + PerFrameGroups* m_frameResultGroups; + /// Mutex to protect the output vector + OFMutex* m_frameResultGroupsMutex; + /// Start frame this thread should handle (inclusive, starts with 0) + /// (i.e.\ the first frame this thread handles is m_startFrame) + Uint32 m_startFrame; + /// End frame this thread should handle (exclusive, i.e.\ the last frame + /// this thread handles is m_endFrame - 1) + Uint32 m_endFrame; + /// Mutex to protect error output + OFMutex* m_errorMutex; + /// Pointer to a condition variable that is set if an error occurs + /// during reading. This is used to signal the main thread that an error + /// occurred during reading. The main thread can then check the error + /// condition variable to see if an error occurred and handle it accordingly. + OFConditionConst* m_errorOccurred; + /// Pointer to the FGInterface instance to read from. This is used to + /// access the FGInterface methods for reading functional groups. + /// It is set by the init() method and used in the run() method to read + /// each functional group + FGInterface* m_fgInterfacePtr; // Pointer to the FGInterface instance to read from + + /** Initialize the thread + * @param perFrameItems Input vector of per-frame items, one item per frame + * containing all functional groups for that frame. + * @param perFrameItemsMutex Mutex to protect the input vector + * @param m_frameResultGroups Output vector of per-frame functional groups, + * one item per frame (index = frame number) containing all functional groups for that frame. + * @param frameResultGroupsMutex Mutex to protect the output vector + * @param startFrame Start frame this thread should handle (inclusive, starts with 0) + * @param endFrame End frame this thread should handle (exclusive, i.e.\ the last frame this thread handles is m_endFrame - 1) + * @param errorMutex Mutex to protect error output + * @param errorOccurred Pointer to a condition variable that is set if an error occurs + * during reading. The main thread can check this variable to see if an error occurred. + * @param fgInterfacePtr Pointer to the FGInterface instance; + * used to access its readSingleFG() method + */ + void init(OFVector* perFrameItems, + OFMutex* perFrameItemsMutex, + PerFrameGroups* m_frameResultGroups, + OFMutex* frameResultGroupsMutex, + Uint32 startFrame, + Uint32 endFrame, + OFMutex* errorMutex, + OFConditionConst* errorOccurred, + FGInterface* fgInterfacePtr); + + /// Default constructor + ThreadedFGReader(); + + /// Destructor + ~ThreadedFGReader(); + + /** Run method, called by OFThread::start() + * This method will read the functional groups for the frames assigned + * to this thread from the input vector and store them in the output vector. + * It will stop in case of an error and set the error condition variable + * to indicate that an error occurred. + */ + void run(); + }; + private: /// Shared functional groups FunctionalGroups m_shared; @@ -295,6 +472,14 @@ private: /// If enabled, functional group structure is checked on write(). Otherwise, /// checks are skipped. OFBool m_checkOnWrite; + + /// Maximum number of threads to use for reading and writing per-frame functional groups, + /// default is 1 thread (sequential writing). The number provided by the user + /// will be adjusted to the number of frames, i.e. there will be not more threads + /// used than one fifth the number of frames (so that each thread must at least handle + // 10 frames, since otherwise the overhead of starting threads would be too high). The + /// number is adjusted on the fly. + Uint32 m_numThreads; }; #endif // MODMULTIFRAMEFGH_H diff --git a/dcmfg/include/dcmtk/dcmfg/fgseg.h b/dcmfg/include/dcmtk/dcmfg/fgseg.h index 19136d9a..3b7cc1b0 100644 --- a/dcmfg/include/dcmtk/dcmfg/fgseg.h +++ b/dcmfg/include/dcmtk/dcmfg/fgseg.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -97,7 +97,7 @@ public: */ virtual OFCondition getReferencedSegmentNumber(Uint16& value, const unsigned long pos = 0); - /** Set Referenced Segment Number + /** Set Referenced Segment Number (starting from 1) * @param segmentNumber Value to be set * @return EC_Normal if successful, an error code otherwise */ diff --git a/dcmfg/include/dcmtk/dcmfg/fgtypes.h b/dcmfg/include/dcmtk/dcmfg/fgtypes.h index 52e849ff..cfdb167f 100644 --- a/dcmfg/include/dcmtk/dcmfg/fgtypes.h +++ b/dcmfg/include/dcmtk/dcmfg/fgtypes.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -102,6 +102,8 @@ extern DCMTK_DCMFG_EXPORT const OFConditionConst FG_EC_InconsistentConcatenation extern DCMTK_DCMFG_EXPORT const OFConditionConst FG_EC_ConcatenationComplete; /// Unsupported pixel data layout extern DCMTK_DCMFG_EXPORT const OFConditionConst FG_EC_UnsupportedPixelDataLayout; +/// Parallel processing failed +extern DCMTK_DCMFG_EXPORT const OFConditionConst FG_EC_ParallelProcessingFailed; /*---------------------* * class declaration * @@ -114,6 +116,13 @@ class DCMTK_DCMFG_EXPORT DcmFGTypes { public: + + // --- Constants --- + + /// Maximum number of frames, limited by + /// Number of Frames attribute which maxes out at 2^31-1. + static const Uint32 DCMFG_MAX_FRAMES = 2147483647; + // --- Type definitions --- /** Functional group types diff --git a/dcmfg/include/dcmtk/dcmfg/framesorter.h b/dcmfg/include/dcmtk/dcmfg/framesorter.h new file mode 100644 index 00000000..e918c9d3 --- /dev/null +++ b/dcmfg/include/dcmtk/dcmfg/framesorter.h @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2015-2025, Open Connections GmbH + * + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation are maintained by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmfg + * + * Author: Andrey Fedorov + * + * Purpose: Abstract base class for sorting frames of a functional group + * + */ + +#ifndef FRAMESORTER_H +#define FRAMESORTER_H + +#include "dcmtk/config/osconfig.h" +#include "dcmtk/ofstd/ofcond.h" +#include "dcmtk/ofstd/ofvector.h" +#include "dcmtk/ofstd/ofstring.h" +#include "dcmtk/ofstd/ofmath.h" +#include "dcmtk/dcmfg/fginterface.h" +#include "dcmtk/dcmfg/fgplanpo.h" +#include "dcmtk/dcmfg/fgplanor.h" +#include "dcmtk/ofstd/ofcond.h" +#include "dcmtk/dcmfg/fgtypes.h" + +#include // for qsort + +typedef OFVector ImagePosition; // Image Position Patient +typedef OFPair FrameWithPos; // DICOM frame number and Image Position Patient + +/** Abstract class for sorting a set of frames in a functional group. The + * sorting criteria are up to the actual implementation classes. + */ +class FrameSorter +{ + +public: + + /** Structure that transports the results of a frame sorting operation + */ + struct Results + { + /** Default constructor, initializes empty results + */ + Results() : + errorCode(EC_Normal), + frameNumbers(), + key(DCM_UndefinedTagKey), + fgSequenceKey(DCM_UndefinedTagKey), + fgPrivateCreator() { } + + /** Clear all results, i.e. reset to default state + */ + void clear() + { + errorCode = EC_Normal; + frameNumbers.clear(); + key = DCM_UndefinedTagKey; + fgSequenceKey = DCM_UndefinedTagKey; + fgPrivateCreator = ""; + } + + /// Error code: EC_Normal if sorting was successful, error code otherwise. + /// The error code should be set in any case (default: EC_Normal) + OFCondition errorCode; + /// The frame numbers, in sorted order (default: empty) + OFVector frameNumbers; + /// The frame positions, in sorted order, if provided + /// by the sorter (default: empty). If not empty, contains the same number + /// of items as frameNumbers. + OFVector framePositions; + /// Tag key that contains the information that was crucial for sorting. + /// This is especially useful for creating dimension indices. Should be + /// set to (0xffff,0xfff) if none was used (default). + DcmTagKey key; + /// Tag functional group sequence key that contains the tag key (see other member) + /// that was crucial for sorting. + /// This is especially useful for creating dimension indices. Should be + /// set to (0xffff,0xfff) if none was used (default). + DcmTagKey fgSequenceKey; + /// Tag functional group sequence's private creator string for the fgSequenceKey + /// result member if fgSequenceKey is a private attributes. + /// This is especially useful for creating dimension indices that base on private + /// attibutes. Should be left empty if fgSequenceKey is not private or fgSequenceKey + /// is not used at all (default). + OFString fgPrivateCreator; + }; + + /** Structure that holds Image Position (Patient) values for frames + */ + struct FramePositions + { + OFVector positions; + }; + + /** Default constructor, does nothing + */ + FrameSorter() + : m_fg(NULL) + {} + + /** Set input data for this sorter + * @param fg The functional groups to work on. Ownership + * of pointer stays with the caller. + */ + void setSorterInput(FGInterface* fg) + { + m_fg = fg; + } + + /** Virtual default desctructor, does nothing + */ + virtual ~FrameSorter() {} + + /** Return a frame order that is determined by the implementation of the particular + * derived class. E.g. a sorting by Plane Position (Patient) could be implemented. + * @param results The results of the sorting procedure. Should be empty (cleared) + * when handed into the function. + */ + virtual void sort(Results& results) =0; + + /** Get description of the sorting algorithm this class uses. + * @return Free text description of the sorting algorithm used. + */ + virtual OFString getDescription() =0; + + + // Derived classes may add further functions, e.g. to provide further parameters, + // like the main dataset, frame data, etc. + + +protected: + + /// Pointer to functional groups to work on. Not owned by this class. + FGInterface* m_fg; +}; + +/** Dummy sorter implementing the FrameSorter interface, + * but not doing any sorting at all. As a result it provides + * a list of frames in their natural order, as found in the underlying + * DICOM dataset. The results will not contain frame position + * to make this implementation as lightweight and "stupid" as possible. + */ +class FrameSorterIdentity : public FrameSorter +{ + +public: + + /** + * Default constructor, does nothing + */ + FrameSorterIdentity(){}; + + /** + * Virtual default destructor, does nothing + */ + virtual ~FrameSorterIdentity() + { + + } + + /** Get description of the sorting algorithm this class uses. + * @return Free text description of the sorting algorithm used. + */ + virtual OFString getDescription() + { + return "Returns frames in the order defined in the functional group, i.e. as defined in the image file"; + } + + /** Performs actual sorting. Does only set Results.frameNumbers and errorCode, + * leaving the rest untouched. + * @param results The results produced by dummy sorter (list of frame numbers as + * found in the underlying DICOM dataset, and EC_Normal as error code) + */ + virtual void sort(Results& results) + { + if (m_fg == NULL) + { + results.errorCode = FG_EC_InvalidData; + return; + } + + size_t numFrames = m_fg->getNumberOfFrames(); + if (numFrames == 0) + { + results.errorCode = FG_EC_NotEnoughItems; + return; + } + + for (Uint32 count = 0; count < numFrames; count++) + { + results.frameNumbers.push_back(count); + } + return; + } + +}; + +/** Sorter implementing the FrameSorter interface that sorts frames + * by their Image Position (Patient) attribute. The sorting is done + * by projecting the Image Position (Patient) on the slice direction + * (as defined by the Image Orientation (Patient) attribute). + */ +class FrameSorterIPP : public FrameSorter +{ +public: + + /** Structure that holds a frame for sorting + */ + struct OrderedFrameItem + { + OrderedFrameItem() : + key(), + frameId() + {} + + /// The key relevant for sorting + Float64 key; + /// The DICOM frame number + Uint32 frameId; + /// The frame position as found in Image Position (Patient) + ImagePosition framePos; + }; + + + /** Default constructor, does nothing + */ + ~FrameSorterIPP(){}; + + /** Get description of the sorting algorithm this class uses. + * @return Free text description of the sorting algorithm used. + */ + OFString getDescription(){ + return "Returns frames in the order defined by projecting the ImagePositionPatient on the slice direction."; + } + + /** Get the slice direction from Image Orientation (Patient) + * @param results The results structure to hold error code in case of failure + */ + void getSliceDirection(Results &results) + { + OFBool isPerFrame; + FGPlaneOrientationPatient *planorfg = OFstatic_cast(FGPlaneOrientationPatient*, + m_fg->get(0, DcmFGTypes::EFG_PLANEORIENTPATIENT, isPerFrame)); + if(!planorfg || isPerFrame){ + results.errorCode = FG_EC_InvalidData; + return; + } + + OFVector dirX, dirY; + OFString orientStr; + for(int i=0;i<3;i++){ + if(planorfg->getImageOrientationPatient(orientStr, i).good()){ + dirX.push_back(atof(orientStr.c_str())); + } else { + results.errorCode = FG_EC_InvalidData; + break; + } + } + for(int i=3;i<6;i++){ + if(planorfg->getImageOrientationPatient(orientStr, i).good()){ + dirY.push_back(atof(orientStr.c_str())); + } else { + results.errorCode = FG_EC_InvalidData; + break; + } + } + + if(results.errorCode != EC_Normal) + return; + + sliceDirection = cross_3d(dirX, dirY); + normalize(sliceDirection); + } + + /** Performs actual sorting. Sets given results. + * @param results The results produced by IPP sorter (list of frame numbers sorted + * by Image Position (Patient) projection on slice direction, and EC_Normal + * as error code in case of success) + */ + void sort(Results& results) + { + if(m_fg == NULL){ + results.errorCode = FG_EC_InvalidData; + return; + } + + getSliceDirection(results); + if(results.errorCode != EC_Normal){ + return; + } + + OFBool isPerFrame; + size_t numFrames = m_fg->getNumberOfFrames(); + if (numFrames > DcmFGTypes::DCMFG_MAX_FRAMES) + { + results.errorCode = FG_EC_TooManyItems; + return; + } + OrderedFrameItem* orderedFrameItems = new OrderedFrameItem[numFrames]; + + for(Uint32 frameId=0;frameIdget(frameId, DcmFGTypes::EFG_PLANEPOSPATIENT, isPerFrame)); + + if(!planposfg || !isPerFrame){ + results.errorCode = FG_EC_InvalidData; + return; + } + + ImagePosition sOrigin; + for(int j=0;j<3;j++){ + OFString planposStr; + if(planposfg->getImagePositionPatient(planposStr, j).good()){ + sOrigin.push_back(atof(planposStr.c_str())); + } else { + results.errorCode = FG_EC_InvalidData; + return; + } + } + + Float64 dist; + dist = dot(sliceDirection, sOrigin); + orderedFrameItems[frameId].key = dist; + orderedFrameItems[frameId].frameId = frameId; + orderedFrameItems[frameId].framePos = sOrigin; + } + + qsort(&orderedFrameItems[0], numFrames, sizeof(OrderedFrameItem), &compareIPPKeys); + + for(Uint32 count=0;count cross_3d(OFVector v1, OFVector v2){ + OFVector result; + result.push_back(v1[1]*v2[2]-v1[2]*v2[1]); + result.push_back(v1[2]*v2[0]-v1[0]*v2[2]); + result.push_back(v1[0]*v2[1]-v1[1]*v2[0]); + + return result; + } + + /** Calculate the dot product of two 3D vectors. + * @param v1 First vector + * @param v2 Second vector + * @return Dot product value + */ + Float64 dot(OFVector v1, OFVector v2){ + return Float64(v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]); + } + + /** Normalize a 3D vector. + * @param v The vector to normalize (output parameter) + */ + void normalize(OFVector &v){ + double norm = OFMath::sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]); + v[0] = v[0]/norm; + v[1] = v[1]/norm; + v[2] = v[2]/norm; + } + + + /** Compare function for qsort to sort OrderedFrameItem by their key. + * @param a Pointer to first OrderedFrameItem + * @param b Pointer to second OrderedFrameItem + * @return Negative value if a < b, zero if a == b, positive if a > b + */ + static int compareIPPKeys(const void *a, const void *b); + + /// The slice direction vector + OFVector sliceDirection; + +}; + +/** Compare function to compare OrderedFrameItem by their key. + * @param a Pointer to first OrderedFrameItem + * @param b Pointer to second OrderedFrameItem + * @return Returns 1 for a > b, -1 otherwise + */ +int FrameSorterIPP::compareIPPKeys(const void *a, const void *b) +{ + FrameSorterIPP::OrderedFrameItem *i1, *i2; + i1 = (FrameSorterIPP::OrderedFrameItem*) a; + i2 = (FrameSorterIPP::OrderedFrameItem*) b; + if(i1->key > i2->key) + return 1; + else + return -1; +} + +#endif // FRAMESORTHER_H diff --git a/dcmfg/libsrc/Makefile.dep b/dcmfg/libsrc/Makefile.dep index 8ed93f8f..7b3bc518 100644 --- a/dcmfg/libsrc/Makefile.dep +++ b/dcmfg/libsrc/Makefile.dep @@ -70,7 +70,11 @@ concatenationcreator.o: concatenationcreator.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ ../include/dcmtk/dcmfg/concatenationcreator.h \ - ../include/dcmtk/dcmfg/fgdefine.h ../include/dcmtk/dcmfg/fgtypes.h + ../include/dcmtk/dcmfg/fgdefine.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../include/dcmtk/dcmfg/fgtypes.h concatenationloader.o: concatenationloader.cc \ ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ @@ -141,6 +145,7 @@ concatenationloader.o: concatenationloader.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h \ @@ -347,6 +352,7 @@ fgctacquisitiondetails.o: fgctacquisitiondetails.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -424,6 +430,7 @@ fgctacquisitiontype.o: fgctacquisitiontype.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -505,6 +512,7 @@ fgctadditionalxraysource.o: fgctadditionalxraysource.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -584,6 +592,7 @@ fgctexposure.o: fgctexposure.cc \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrfd.h \ @@ -667,6 +676,7 @@ fgctgeometry.o: fgctgeometry.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -743,6 +753,7 @@ fgctimageframetype.o: fgctimageframetype.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -817,6 +828,7 @@ fgctposition.o: fgctposition.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -897,6 +909,7 @@ fgctreconstruction.o: fgctreconstruction.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -972,6 +985,7 @@ fgcttabledynamics.o: fgcttabledynamics.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -1052,6 +1066,7 @@ fgctxraydetails.o: fgctxraydetails.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -1118,21 +1133,23 @@ fgderimg.o: fgderimg.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ - ../include/dcmtk/dcmfg/fgderimg.h ../include/dcmtk/dcmfg/fgbase.h \ - ../include/dcmtk/dcmfg/fgtypes.h ../include/dcmtk/dcmfg/fgdefine.h \ + ../include/dcmtk/dcmfg/fgderimg.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../include/dcmtk/dcmfg/fgbase.h ../include/dcmtk/dcmfg/fgtypes.h \ + ../include/dcmtk/dcmfg/fgdefine.h \ ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrst.h \ @@ -1222,6 +1239,7 @@ fgfact.o: fgfact.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ ../include/dcmtk/dcmfg/fgctgeometry.h \ @@ -1231,7 +1249,6 @@ fgfact.o: fgfact.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmfg/fgcttabledynamics.h \ ../include/dcmtk/dcmfg/fgctxraydetails.h \ ../include/dcmtk/dcmfg/fgderimg.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrst.h \ ../include/dcmtk/dcmfg/fgfact.h ../include/dcmtk/dcmfg/fgfracon.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrdt.h \ @@ -1256,6 +1273,7 @@ fgfact.o: fgfact.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmfg/fgtemporalposition.h \ ../include/dcmtk/dcmfg/fgusimagedescription.h \ ../../dcmiod/include/dcmtk/dcmiod/iodutil.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h fgfracon.o: fgfracon.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ @@ -1339,6 +1357,7 @@ fgfracon.o: fgfracon.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h fgframeanatomy.o: fgframeanatomy.cc \ ../../config/include/dcmtk/config/osconfig.h \ @@ -1417,6 +1436,7 @@ fgframeanatomy.o: fgframeanatomy.cc \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ ../../dcmiod/include/dcmtk/dcmiod/iodutil.h \ @@ -1500,6 +1520,7 @@ fgframevoilut.o: fgframevoilut.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -1578,6 +1599,7 @@ fgimagedatatype.o: fgimagedatatype.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -1721,6 +1743,7 @@ fgirradiationeventid.o: fgirradiationeventid.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -1797,6 +1820,7 @@ fgparametricmapframetype.o: fgparametricmapframetype.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -1876,6 +1900,7 @@ fgpixeltransform.o: fgpixeltransform.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -1951,6 +1976,7 @@ fgpixmsr.o: fgpixmsr.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -2027,6 +2053,7 @@ fgplanor.o: fgplanor.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -2102,6 +2129,7 @@ fgplanorvol.o: fgplanorvol.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -2178,6 +2206,7 @@ fgplanpo.o: fgplanpo.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -2253,6 +2282,7 @@ fgplanposvol.o: fgplanposvol.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -2334,6 +2364,7 @@ fgrealworldvaluemapping.o: fgrealworldvaluemapping.cc \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrss.h \ @@ -2414,6 +2445,7 @@ fgseg.o: fgseg.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -2489,6 +2521,7 @@ fgtemporalposition.o: fgtemporalposition.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -2605,6 +2638,7 @@ fgusimagedescription.o: fgusimagedescription.cc \ ../include/dcmtk/dcmfg/fgdefine.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../dcmiod/include/dcmtk/dcmiod/iodutil.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ @@ -2712,4 +2746,5 @@ stackinterface.o: stackinterface.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ - ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h + ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def diff --git a/dcmfg/libsrc/concatenationcreator.cc b/dcmfg/libsrc/concatenationcreator.cc index 7143b0bc..764a3e69 100644 --- a/dcmfg/libsrc/concatenationcreator.cc +++ b/dcmfg/libsrc/concatenationcreator.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019-2024, Open Connections GmbH + * Copyright (C) 2019-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -30,7 +30,6 @@ #include "dcmtk/dcmfg/concatenationcreator.h" #include "dcmtk/dcmfg/fgtypes.h" - // Maximum number of instances that make up a Concatenation const Uint16 ConcatenationCreator::m_MAX_INSTANCES_PER_CONCATENATION = 65535; @@ -69,37 +68,48 @@ ConcatenationCreator::~ConcatenationCreator() if (m_cfgTransferOwnership) { delete m_srcDataset; - delete[] m_srcPixelData; } + // knows whether to free pixel data or not + delete m_srcPixelData; } OFCondition ConcatenationCreator::setCfgInput(DcmItem* srcDataset, OFBool transferOwnership) { // Check pixel data exists and is in native format (i.e. uncompressed) - DcmElement* elem = NULL; - srcDataset->findAndGetElement(DCM_PixelData, elem); - if (!elem) - return FG_EC_PixelDataMissing; - DcmPixelData* pixDataElem = OFstatic_cast(DcmPixelData*, elem); - if (!pixDataElem) - return FG_EC_PixelDataMissing; - if (!pixDataElem->canWriteXfer(EXS_LittleEndianExplicit, EXS_LittleEndianExplicit /* ignored */)) - return EC_UnsupportedEncoding; - OFCondition result = pixDataElem->getUint8Array(m_srcPixelData); - if (!m_srcPixelData || result.bad()) - return FG_EC_PixelDataMissing; + OFCondition result = initSrcPixelData(srcDataset, transferOwnership); + if (result.good()) + { + m_srcDataset = srcDataset; + m_cfgTransferOwnership = transferOwnership; + } - m_srcDataset = srcDataset; + return result; +} +OFCondition ConcatenationCreator::setCfgInput(DcmItem* srcDataset, + Uint8* pixelData, + size_t pixelDataLength, + OFBool transferOwnership) +{ + // Check input parameters + if (!pixelData) + return FG_EC_PixelDataMissing; + + m_srcDataset = srcDataset; + delete m_srcPixelData; + m_srcPixelData = NULL; + m_srcPixelData = new DcmIODTypes::Frame(pixelData, pixelDataLength); + m_srcPixelData->setReleaseMemory(transferOwnership); m_cfgTransferOwnership = transferOwnership; // All fine return EC_Normal; } + OFCondition ConcatenationCreator::setCfgInput(DcmItem* srcDataset, - Uint8* pixelData, - size_t /* pixelDataLength */, + Uint16* pixelData, + size_t pixelDataLength, OFBool transferOwnership) { // Check input parameters @@ -107,13 +117,17 @@ OFCondition ConcatenationCreator::setCfgInput(DcmItem* srcDataset, return FG_EC_PixelDataMissing; m_srcDataset = srcDataset; - m_srcPixelData = pixelData; + delete m_srcPixelData; + m_srcPixelData = NULL; + m_srcPixelData = new DcmIODTypes::Frame(pixelData, pixelDataLength); + m_srcPixelData->setReleaseMemory(transferOwnership); m_cfgTransferOwnership = transferOwnership; // All fine return EC_Normal; } + OFCondition ConcatenationCreator::setCfgFramesPerInstance(Uint32 numFramesPerInstance) { m_cfgNumFramesPerInstance = numFramesPerInstance; @@ -173,9 +187,10 @@ OFCondition ConcatenationCreator::writeNextInstance(DcmItem& dstDataset) { return EC_MemoryExhausted; } + // Setting VR is necessary if Pixel Data is actually 16 bit dstPixelData->setVR(m_VRPixelData); size_t srcPos = (m_numBitsFrame * m_currentSrcFrame) / 8; - memcpy(dstData, &m_srcPixelData[srcPos], numTotalBytesInstance); + memcpy(dstData, &(OFstatic_cast(Uint8*, m_srcPixelData->getPixelData())[srcPos]), numTotalBytesInstance); result = dstDataset.insert(dstPixelData.release()); if (result.good()) { @@ -489,3 +504,45 @@ OFCondition ConcatenationCreator::configureCommon() m_configured = OFTrue; return result; } + + +OFCondition ConcatenationCreator::initSrcPixelData(DcmItem* srcDataset, OFBool transferOwnership) +{ + // Check pixel data exists and is in native format (i.e. uncompressed) + DcmElement* elem = NULL; + srcDataset->findAndGetElement(DCM_PixelData, elem); + if (!elem) + return FG_EC_PixelDataMissing; + DcmPixelData* pixDataElem = OFstatic_cast(DcmPixelData*, elem); + if (!pixDataElem) + return FG_EC_PixelDataMissing; + if (!pixDataElem->canWriteXfer(EXS_LittleEndianExplicit, EXS_LittleEndianExplicit /* ignored */)) + return EC_UnsupportedEncoding; + size_t pixDataLength = pixDataElem->getLength(); + Uint16 bitsAllocated = 0; + srcDataset->findAndGetUint16(DCM_BitsAllocated, bitsAllocated); + if ((bitsAllocated != 8) && (bitsAllocated != 16) && (bitsAllocated != 1)) + return FG_EC_UnsupportedPixelDataLayout; + + OFCondition result; + if (bitsAllocated <= 8) + { + Uint8 *pixelData = NULL; + result = pixDataElem->getUint8Array(pixelData); + if (!pixelData || result.bad()) + return FG_EC_PixelDataMissing; + m_srcPixelData = new DcmIODTypes::Frame(pixelData, pixDataLength); + } + else // bits allocated = 16 + { + Uint16 *pixelData = NULL; + result = pixDataElem->getUint16Array(pixelData); + if (!pixelData || result.bad()) + return FG_EC_PixelDataMissing; + m_srcPixelData = new DcmIODTypes::Frame(pixelData, pixDataLength); + } + m_srcPixelData->setReleaseMemory(transferOwnership); + return EC_Normal; + + +} diff --git a/dcmfg/libsrc/concatenationloader.cc b/dcmfg/libsrc/concatenationloader.cc index d7a81f56..8c7b6e0f 100644 --- a/dcmfg/libsrc/concatenationloader.cc +++ b/dcmfg/libsrc/concatenationloader.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019-2024, Open Connections GmbH + * Copyright (C) 2019-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -223,7 +223,7 @@ const ConcatenationLoader::TScanFailures& ConcatenationLoader::getFailedFiles() } OFCondition -ConcatenationLoader::load(const OFString& concatenationUID, DcmDataset* dataset, OFVector& frames) +ConcatenationLoader::load(const OFString& concatenationUID, DcmDataset* dataset, OFVector& frames) { if (dataset == NULL) return EC_IllegalParameter; @@ -333,33 +333,55 @@ OFCondition ConcatenationLoader::deleteConcatAttributes(DcmItem& item) OFCondition ConcatenationLoader::extractFrames(DcmItem& item, Info& info, const Uint32 numFrames) { - const Uint8* pixData = NULL; - OFCondition result = item.findAndGetUint8Array(DCM_PixelData, pixData); - if (result.good() && pixData) + const Uint8* pixData8 = NULL; + const Uint16* pixData16 = NULL; + OFCondition result; + if (info.m_BitsAlloc <= 8) + { + result = item.findAndGetUint8Array(DCM_PixelData, pixData8); + } + else if (info.m_BitsAlloc == 16) + { + result = item.findAndGetUint16Array(DCM_PixelData, pixData16); + } + else + { + DCMFG_ERROR("Bits Allocated=" << info.m_BitsAlloc << " not supported, must be 1, 8 or 16"); + return FG_EC_UnsupportedPixelDataLayout; + } + if (result.good() && (pixData8 || pixData16)) { size_t bytesPerFrame = 0; result = computeBytesPerFrame(info.m_Rows, info.m_Cols, info.m_BitsAlloc, bytesPerFrame); if (result.good()) { - const Uint8* ptr = pixData; for (Uint32 f = 0; f < numFrames; f++) { - DcmIODTypes::Frame* frame = new DcmIODTypes::Frame(); - if (frame) + DcmIODTypes::FrameBase* frame = NULL; + if (info.m_BitsAlloc <= 8) // 8 or 1 { - frame->length = bytesPerFrame; - frame->pixData = new Uint8[frame->length]; - if (frame->pixData) + frame = new DcmIODTypes::Frame(bytesPerFrame); + if (frame && frame->getPixelData()) { - memcpy(frame->pixData, ptr, frame->length); - ptr += frame->length; - m_Frames.push_back(frame); + const Uint8* ptr = pixData8 + f * frame->getLengthInBytes(); + memcpy(frame->getPixelData(), ptr, frame->getLengthInBytes()); } - else + } + else if (info.m_BitsAlloc == 16) + { + frame = new DcmIODTypes::Frame(bytesPerFrame / 2); + if (frame && frame->getPixelData()) { - result = EC_MemoryExhausted; + // getLength() returns size in bytes, so divide by 2 since we advance by word + const Uint16* ptr = pixData16 + f * frame->getLengthInBytes() / 2; + // memcpy expects number of bytes to copy + memcpy(frame->getPixelData(), ptr, frame->getLengthInBytes()); } } + if (frame) + { + m_Frames.push_back(frame); + } else { result = EC_MemoryExhausted; @@ -401,7 +423,7 @@ OFCondition ConcatenationLoader::computeBytesPerFrame(const Uint16 rows, // This is only different if bits allocated equals 1, which can happen // for binary segmentations or black and white secondary capture objects // (second SC generation). - // Other values than Bits Allocated 16 or 8 are not supported. + // Other values than Bits Allocated 16, 8 or 1 are not supported. bytes_per_frame = bitsAlloc * cols * rows; if ((bitsAlloc == 16) || (bitsAlloc == 8)) { diff --git a/dcmfg/libsrc/fg.cc b/dcmfg/libsrc/fg.cc index 401a1f23..afa8d5a9 100644 --- a/dcmfg/libsrc/fg.cc +++ b/dcmfg/libsrc/fg.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2019, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -45,6 +45,11 @@ void FunctionalGroups::clear() } } +size_t FunctionalGroups::size() const +{ + return m_groups.size(); +} + FGBase* FunctionalGroups::find(const DcmFGTypes::E_FGType fgType) { FGBase* group = NULL; diff --git a/dcmfg/libsrc/fgderimg.cc b/dcmfg/libsrc/fgderimg.cc index 84ddd0a1..4eccd9cf 100644 --- a/dcmfg/libsrc/fgderimg.cc +++ b/dcmfg/libsrc/fgderimg.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2016-2024, Open Connections GmbH + * Copyright (C) 2016-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -242,6 +242,7 @@ OFCondition FGDerivationImage::write(DcmItem& item) SourceImageItem::SourceImageItem() : m_PurposeOfReferenceCode() , m_ImageSOPInstanceReference() + , m_SpatialLocationsPreserved(DCM_SpatialLocationsPreserved) { } @@ -254,6 +255,7 @@ SourceImageItem& SourceImageItem::operator=(const SourceImageItem& rhs) { m_PurposeOfReferenceCode = rhs.m_PurposeOfReferenceCode; m_ImageSOPInstanceReference = rhs.m_ImageSOPInstanceReference; + m_SpatialLocationsPreserved = rhs.m_SpatialLocationsPreserved; return *this; } @@ -261,6 +263,7 @@ void SourceImageItem::clearData() { m_PurposeOfReferenceCode.clearData(); m_ImageSOPInstanceReference.clear(); + m_SpatialLocationsPreserved.clear(); } OFCondition SourceImageItem::check() const @@ -281,6 +284,10 @@ int SourceImageItem::compare(const SourceImageItem& rhs) const { result = this->m_ImageSOPInstanceReference.compare(rhs.m_ImageSOPInstanceReference); } + if (result != 0) + { + result = this->m_SpatialLocationsPreserved.compare(rhs.m_SpatialLocationsPreserved); + } return result; } @@ -295,12 +302,29 @@ ImageSOPInstanceReferenceMacro& SourceImageItem::getImageSOPInstanceReference() return m_ImageSOPInstanceReference; } +OFCondition SourceImageItem::getSpatialLocationsPreserved(OFString& value, const long signed int pos) const +{ + return DcmIODUtil::getStringValueFromElement(m_SpatialLocationsPreserved, value, pos); +} + +OFCondition SourceImageItem::setSpatialLocationsPreserved(const OFString& value, const OFBool checkValue) +{ + OFCondition result = (checkValue) ? DcmCodeString::checkStringValue(value, "1") : EC_Normal; + if (result.good()) + result = m_SpatialLocationsPreserved.putOFStringArray(value); + return result; +} + + OFCondition SourceImageItem::read(DcmItem& itemOfSourceImageSequence, const OFBool clearOldData) { /* Re-initialize object */ if (clearOldData) clearData(); + /* Spatial Locations Preserved */ + DcmIODUtil::getAndCheckElementFromDataset(itemOfSourceImageSequence, m_SpatialLocationsPreserved, "1", "3", "DerivationImageMacro"); + /* Read Purpose of Reference Code Sequence */ DcmIODUtil::readSingleItem(itemOfSourceImageSequence, DCM_PurposeOfReferenceCodeSequence, @@ -332,6 +356,13 @@ OFCondition SourceImageItem::write(DcmItem& itemOfSourceImageSequence) m_ImageSOPInstanceReference.write(itemOfSourceImageSequence); } + /** Currently only writes Spatial Locations Preserved */ + if (result.good()) + { + result = DcmIODUtil::copyElementToDataset( + result, itemOfSourceImageSequence, m_SpatialLocationsPreserved, "1" /* vm */, "3" /*requirement type*/, "DerivationImageMacro"); + } + return result; } diff --git a/dcmfg/libsrc/fginterface.cc b/dcmfg/libsrc/fginterface.cc index 046d9116..72418c43 100644 --- a/dcmfg/libsrc/fginterface.cc +++ b/dcmfg/libsrc/fginterface.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -25,14 +25,221 @@ #include "dcmtk/dcmfg/fgfact.h" // for creating new functional groups #include "dcmtk/dcmfg/fginterface.h" #include "dcmtk/ofstd/ofmap.h" +#include "dcmtk/ofstd/ofthread.h" #include "dcmtk/ofstd/ofmem.h" #include "dcmtk/dcmdata/dcdeftag.h" #include "dcmtk/dcmdata/dcsequen.h" + +// ---------------------------------- ThreadedFGWriter ---------------------------------- +// This class is used to write functional groups in parallel for a group of frames, each. +FGInterface::ThreadedFGWriter::ThreadedFGWriter() + : OFThread() + , m_frameGroups(OFnullptr) + , m_perFrameResultItems(OFnullptr) + , m_startFrame(0) + , m_endFrame(0) + , m_errorMutex(OFnullptr) +{ +} + +void FGInterface::ThreadedFGWriter::init(OFVector >* frameGroups, + OFVector* perFrameResultItems, + OFMutex* perFrameResultItemsMutex, + const Uint32 startFrame, + const Uint32 endFrame, + OFConditionConst* errorOccurred, + OFMutex* errorMutex) +{ + // Store the parameters in member variables + m_frameGroups = frameGroups; + m_perFrameResultItems = perFrameResultItems; + m_perFrameResultItemsMutex = perFrameResultItemsMutex; + m_startFrame = startFrame; + m_endFrame = endFrame; + m_errorOccurred = errorOccurred; + m_errorMutex = errorMutex; +} + + +FGInterface::ThreadedFGWriter::~ThreadedFGWriter() +{ + // Nothing to do here +} + +void FGInterface::ThreadedFGWriter::run() +{ + if (!m_frameGroups || !m_perFrameResultItems) + { + DCMFG_ERROR("ThreadedFGWriter: Not properly initialized, cannot run."); + m_errorMutex->lock(); + *m_errorOccurred = FG_EC_ParallelProcessingFailed; + m_errorMutex->unlock(); + return; + } + + // Iterate over all frames and write the functional groups + for (Uint32 idx = m_startFrame; idx < m_endFrame; ++idx) + { + Uint32 frameNo = m_frameGroups->at(idx).first; + FunctionalGroups* fgPtr = m_frameGroups->at(idx).second; + OFunique_ptr perFrameItem(new DcmItem()); + + FunctionalGroups::iterator groupIt = fgPtr->begin(); + while ((groupIt != fgPtr->end())) + { + m_errorMutex->lock(); + if (*m_errorOccurred != EC_Normal) + { + m_errorMutex->unlock(); + DCMFG_ERROR("Error occurred, stopping further processing."); + return; + } + m_errorMutex->unlock(); + DCMFG_DEBUG("Writing per-frame group: " + << DcmFGTypes::FGType2OFString((*groupIt).second->getType()) + << " for frame #" << frameNo); + OFCondition result = (*groupIt).second->write(*perFrameItem); + groupIt++; + if (result.bad()) + { + DCMFG_ERROR("Error writing functional group for frame #" << frameNo << ": " << result.text()); + m_errorMutex->lock(); + *m_errorOccurred = result.condition(); // Store the error + m_errorMutex->unlock(); + return; + } + } + // Lock the mutex before modifying shared data + m_perFrameResultItemsMutex->lock(); + (*m_perFrameResultItems)[idx] = perFrameItem.release(); + m_perFrameResultItemsMutex->unlock(); + } +} + + +// ---------------------------------- ThreadedFGWReader ---------------------------------- + +FGInterface::ThreadedFGReader::ThreadedFGReader() + : OFThread() + , m_perFrameItems(OFnullptr) + , m_frameResultGroups(OFnullptr) + , m_frameResultGroupsMutex(OFnullptr) + , m_startFrame(0) + , m_endFrame(0) + , m_errorMutex(OFnullptr) + , m_errorOccurred(OFnullptr) +{ +} + + +void FGInterface::ThreadedFGReader::init(OFVector* perFrameItems, + OFMutex* perFrameItemsMutex, + PerFrameGroups* frameResultGroups, + OFMutex* frameResultGroupsMutex, + Uint32 startFrame, + Uint32 endFrame, + OFMutex* errorMutex, + OFConditionConst* errorOccurred, + FGInterface* fgInterfacePtr) +{ + m_perFrameItems = perFrameItems; + m_perFrameItemsMutex = perFrameItemsMutex; + m_frameResultGroups = frameResultGroups; + m_frameResultGroupsMutex = frameResultGroupsMutex; + m_startFrame = startFrame; + m_endFrame = endFrame; + m_errorMutex = errorMutex; + m_errorOccurred = errorOccurred; + m_fgInterfacePtr = fgInterfacePtr; +} + +FGInterface::ThreadedFGReader::~ThreadedFGReader() +{ + // Nothing to do here +} + + +void FGInterface::ThreadedFGReader::run() +{ + if (!m_perFrameItems || !m_frameResultGroups) + { + DCMFG_ERROR("ThreadedFGReader: Not properly initialized, cannot run."); + m_errorMutex->lock(); + *m_errorOccurred = FG_EC_ParallelProcessingFailed; + m_errorMutex->unlock(); + return; + } + + // Iterate over all frames and read the functional groups + for (Uint32 idx = m_startFrame; idx < m_endFrame; ++idx) + { + Uint32 frameNo = idx; + // No need to lock for reading from m_perFrameItems, as each thread only reads its assigned range + if (frameNo >= m_perFrameItems->size()) + { + DCMFG_ERROR("Frame index " << frameNo << " out of bounds for per-frame items vector"); + m_errorMutex->lock(); + *m_errorOccurred = FG_EC_ParallelProcessingFailed; + m_errorMutex->unlock(); + return; + } + DcmItem* perFrameItem = (*m_perFrameItems)[frameNo]; + if (!perFrameItem) + { + DCMFG_ERROR("No per-frame item found for frame #" << frameNo); + m_errorMutex->lock(); + *m_errorOccurred = FG_EC_ParallelProcessingFailed; // Store the error + m_errorMutex->unlock(); + return; + } + + DCMFG_DEBUG("Reading per-frame groups for frame #" << frameNo); + // Create a new FunctionalGroups object to store the read groups + OFunique_ptr groupsOneFrame(new FunctionalGroups()); + if (!groupsOneFrame) + { + DCMFG_ERROR("Memory exhausted while creating FunctionalGroups object for frame #" << frameNo); + m_errorMutex->lock(); + *m_errorOccurred = EC_MemoryExhausted; // Store the error + m_errorMutex->unlock(); + return; + } + // Read the functional groups from the per-frame item + // Remove parent item since otherwise the code will try to access the parent item + // to find the Specific Character Set, and this will crash if multiple threads + // try to access the same parent item. + DcmSequenceOfItems* perFrameFGSeq = OFstatic_cast(DcmSequenceOfItems*, perFrameItem->getParent()); + perFrameItem->setParent(OFnullptr); + OFCondition result = m_fgInterfacePtr->readSingleFG(*perFrameItem, *groupsOneFrame); + perFrameItem->setParent(perFrameFGSeq); // Restore parent item + if (result.bad()) + { + DCMFG_ERROR("Error reading functional groups for frame #" << frameNo << ": " << result.text()); + m_errorMutex->lock(); + *m_errorOccurred = result.condition(); // Store the error + m_errorMutex->unlock(); + return; + } + // Lock the mutex before modifying shared data + m_frameResultGroupsMutex->lock(); + // cast is safe since we check earlier that it is not + // larger than DcmFGTypes::DCMFG_MAX_FRAMES (2^32-1) + m_frameResultGroups->insert(OFMake_pair(frameNo, groupsOneFrame.release())); + m_frameResultGroupsMutex->unlock(); + DCMFG_DEBUG("Finished reading functional groups for frame #" << frameNo); + } +} + + + +// ----------------------------------- FGInterface ----------------------------------- + FGInterface::FGInterface() : m_shared() , m_perFrame() , m_checkOnWrite(OFTrue) + , m_numThreads(1) // Default to 1 thread { } @@ -238,12 +445,36 @@ OFCondition FGInterface::readPerFrameFG(DcmItem& dataset) size_t numFrames = perFrame->card(); if (numFrames == 0) { - DCMFG_WARN("No Item in Shared Functional Group Sequence but exactly one or more expected"); + DCMFG_WARN("No Item in Per-Frame Functional Group Sequence but exactly one or more expected"); return FG_EC_NoPerFrameFG; } + if (numFrames > DcmFGTypes::DCMFG_MAX_FRAMES) + { + DCMFG_ERROR("Too many items in Per-Frame Functional Group Sequence: " << numFrames + << ", maximum is " << DcmFGTypes::DCMFG_MAX_FRAMES); + return FG_EC_TooManyItems; + } + // We want to either read sequentially or in parallel, depending on the number of threads. + // Cast is safe since e checked range above. + Uint32 threadsUsed = findAdequateNumberOfThreads(OFstatic_cast(Uint32, numFrames), m_numThreads); + if (threadsUsed > 1) + { + result = readPerFrameFGParallel(*perFrame, m_numThreads); + } + else + { + result = readPerFrameFGSequential(*perFrame); + } + return EC_Normal; // for now we always return EC_Normal... +} + + +OFCondition FGInterface::readPerFrameFGSequential(DcmSequenceOfItems& perFrameFGSeq) +{ /* Read functional groups for each item (one per frame) */ - DcmItem* oneFrameItem = OFstatic_cast(DcmItem*, perFrame->nextInContainer(NULL)); + OFCondition result; + DcmItem* oneFrameItem = OFstatic_cast(DcmItem*, perFrameFGSeq.nextInContainer(NULL)); Uint32 count = 0; while (oneFrameItem != NULL) { @@ -271,12 +502,86 @@ OFCondition FGInterface::readPerFrameFG(DcmItem& dataset) DCMFG_ERROR("Could not read functional groups for frame #" << count << ": " << result.text()); } } - oneFrameItem = OFstatic_cast(DcmItem*, perFrame->nextInContainer(oneFrameItem)); + oneFrameItem = OFstatic_cast(DcmItem*, perFrameFGSeq.nextInContainer(oneFrameItem)); count++; } return EC_Normal; // for now we always return EC_Normal... } + +OFCondition FGInterface::readPerFrameFGParallel(DcmSequenceOfItems& perFrameFGSeq, const Uint32 numThreads) +{ + DCMFG_DEBUG("Reading per-frame functional groups in parallel using " << numThreads << " threads"); + // Read functional groups for each item (one per frame) + OFCondition result; + size_t numFrames = perFrameFGSeq.card(); + + // Create a vector to hold the functional groups for each frame and protect it with a mutex + PerFrameGroups& perFrameResultGroups = m_perFrame; + OFMutex perFrameResultGroupsMutex; + + // Create a vector to hold the results of all threads, protected by a mutex + OFMutex perFrameInputMutex; + OFVector perFrameInputItems(numFrames, NULL); + // Fill the vector with items from the sequence + DcmItem* oneFrameItem = OFstatic_cast(DcmItem*, perFrameFGSeq.nextInContainer(NULL)); + size_t count = 0; + while (oneFrameItem != NULL) + { + // Store the item in the vector + perFrameInputItems[count] = oneFrameItem; + oneFrameItem = OFstatic_cast(DcmItem*, perFrameFGSeq.nextInContainer(oneFrameItem)); + count++; + } + + // Create a mutex for error handling + OFConditionConst errorOccurred = EC_Normal; + OFMutex errorMutex; + + // Create and start threads + OFVector threads(numThreads); + // Range check on numFrames has been performed earlier so computation should be safe + Uint32 framesPerThread = OFstatic_cast(Uint32, (numFrames + numThreads - 1) / numThreads); + for (Uint32 i = 0; i < numThreads; ++i) + { + threads[i] = new ThreadedFGReader(); + Uint32 startFrame = i * framesPerThread; + // numFrame is range-checked earlier + Uint32 endFrame = (startFrame + framesPerThread < numFrames) ? startFrame + framesPerThread : OFstatic_cast(Uint32, numFrames); + + threads[i]->init(&perFrameInputItems, &perFrameInputMutex, + &perFrameResultGroups, &perFrameResultGroupsMutex, + startFrame, + endFrame, + &errorMutex, &errorOccurred, this); + } + // Start all threads + for (Uint32 i = 0; i < numThreads; ++i) + { + threads[i]->start(); + } + + // Wait for all threads to finish + for (Uint32 i = 0; i < numThreads; ++i) + { + threads[i]->join(); + delete threads[i]; + threads[i] = NULL; + } + + // Check if any thread encountered an error + if (errorOccurred != EC_Normal) + { + DCMFG_ERROR("Error occurred while reading functional groups in parallel: " << OFCondition(errorOccurred).text()); + return errorOccurred; + } + + // Store the results in m_perFrame + return EC_Normal; +} + + + OFCondition FGInterface::readSingleFG(DcmItem& fgItem, FunctionalGroups& groups) { OFCondition result; @@ -339,6 +644,8 @@ OFCondition FGInterface::write(DcmItem& dataset) // Write shared functional Groups OFCondition result = writeSharedFG(dataset); + DcmItem* seqItem; + dataset.findAndGetSequenceItem(DCM_SharedFunctionalGroupsSequence, seqItem); // Write per frame functional groups if (result.good()) result = writePerFrameFG(dataset); @@ -443,6 +750,23 @@ OFBool FGInterface::getCheckOnWrite() return m_checkOnWrite; } +void FGInterface::setUseThreads(const Uint32 numThreads) +{ + if (numThreads > 0) + { + m_numThreads = numThreads; + } + else + { + m_numThreads = 1; // Fallback to single-threaded mode + } +} + +Uint32 FGInterface::getUseThreads() const +{ + return m_numThreads; +} + FunctionalGroups* FGInterface::getOrCreatePerFrameGroups(const Uint32 frameNo) { OFMap::iterator it = m_perFrame.find(frameNo); @@ -469,7 +793,45 @@ FunctionalGroups* FGInterface::getOrCreatePerFrameGroups(const Uint32 frameNo) return fg; } + OFCondition FGInterface::writePerFrameFG(DcmItem& dataset) +{ + OFCondition result; + + DCMFG_DEBUG("Writing per-frame functional groups"); + result = dataset.insertEmptyElement(DCM_PerFrameFunctionalGroupsSequence, OFTrue); // start with empty sequence + if (result.bad()) + { + DCMFG_ERROR("Could not create Per-frame Functional Groups Sequence"); + return result; + } + + /* Iterate over frames */ + size_t numFrames = m_perFrame.size(); + if (numFrames > DcmFGTypes::DCMFG_MAX_FRAMES) + { + DCMFG_ERROR("Too many frames in Per-frame Functional Groups: " << numFrames + << ", maximum is " << DcmFGTypes::DCMFG_MAX_FRAMES); + return FG_EC_TooManyItems; + } + + // Use parallel processing for writing functional groups, if desired and adequate. + // Cast is safe since we checked range above. + Uint32 threadsUsed = findAdequateNumberOfThreads(OFstatic_cast(Uint32, numFrames), m_numThreads); + if (threadsUsed > 1) + { + result = writePerFrameFGParallel(dataset, m_numThreads); + } + else + { + // Fallback to sequential processing for small datasets or single-threaded environment + result = writePerFrameFGSequential(dataset); + } + + return result; +} + +OFCondition FGInterface::writePerFrameFGSequential(DcmItem& dataset) { DCMFG_DEBUG("Writing per-frame functional groups"); OFCondition result @@ -509,6 +871,91 @@ OFCondition FGInterface::writePerFrameFG(DcmItem& dataset) return result; } + +OFCondition FGInterface::writePerFrameFGParallel(DcmItem& dataset, const Uint32 numThreads) +{ + DCMFG_DEBUG("Writing per-frame functional groups in parallel using " << numThreads << " threads"); + OFConditionConst errorOccurred(EC_Normal); + OFMutex errorMutex; + OFMutex perFrameItemsMutex; + + // Prepare a vector of frame numbers and their corresponding FunctionalGroups pointers. + // We need this to distribute the work across threads. Using the OFMap directly in threads + // could lead to data races and inconsistencies since each thread would invalidate the iterators + // being used in the other threads. + OFVector > frameGroups; + frameGroups.reserve(m_perFrame.size()); + for (OFMap::iterator it = m_perFrame.begin(); it != m_perFrame.end(); ++it) + { + frameGroups.push_back(OFMake_pair((*it).first, (*it).second)); + } + // We check earlier that number of frames is not larger than max allowed + Uint32 numFrames = OFstatic_cast(Uint32, frameGroups.size()); + + // Prepare output vector for per-frame items + OFVector perFrameItems(numFrames, NULL); + + // Thread pool + OFVector threads; + threads.reserve(numThreads); + + Uint32 framesPerThread = (numFrames + numThreads - 1) / numThreads; + + for (Uint32 threadIndex = 0; threadIndex < numThreads; ++threadIndex) + { + Uint32 startFrame = threadIndex * framesPerThread; + Uint32 endFrame = (startFrame + framesPerThread < numFrames) ? startFrame + framesPerThread : numFrames; + + ThreadedFGWriter* writer = new ThreadedFGWriter(); + writer->init(&frameGroups, &perFrameItems, &perFrameItemsMutex, startFrame, endFrame, &errorOccurred, &errorMutex); + threads.push_back(writer); + writer->start(); // Start the thread + } + + // Wait for all threads to complete + OFVector::iterator thread = threads.begin(); + while (thread != threads.end()) + { + (*thread)->join(); + delete (*thread); // Clean up the thread object + thread++; + } + threads.clear(); + + if (errorOccurred == EC_Normal) + { + DCMFG_DEBUG("All threads completed successfully, inserting per-frame items into the dataset"); + } + else + { + DCMFG_ERROR("Error occurred in one or more threads: " << OFCondition(errorOccurred).text()); + return OFCondition(errorOccurred); + } + // insert all per-frame items into the dataset + OFCondition result; + for (size_t idx = 0; idx < perFrameItems.size(); ++idx) + { + DcmItem* perFrameItem = perFrameItems[idx]; + Uint32 frameNo = frameGroups[idx].first; + if (perFrameItem != NULL) + { + DCMFG_DEBUG("Inserting per-frame item for frame #" << frameNo); + result = dataset.insertSequenceItem(DCM_PerFrameFunctionalGroupsSequence, perFrameItem, OFstatic_cast(long, frameNo)); + if (result.bad()) + { + DCMFG_ERROR("Error inserting per-frame item for frame #" << frameNo << ": " << result.text()); + break; + } + } + else + { + DCMFG_ERROR("Per-frame item for frame #" << frameNo << " is NULL, cannot insert into dataset"); + } + } + return result; // Return the result of the last insertion +} + + OFCondition FGInterface::writeSharedFG(DcmItem& dataset) { DCMFG_DEBUG("Writing shared functional groups"); @@ -524,7 +971,7 @@ OFCondition FGInterface::writeSharedFG(DcmItem& dataset) DCMFG_ERROR("Could not create Shared Functional Groups Sequence with single item"); return result; } - + DCMFG_DEBUG("Writing " << m_shared.size() << " shared functional groups to item"); FunctionalGroups::iterator it = m_shared.begin(); FunctionalGroups::iterator end = m_shared.end(); while ((it != end) && result.good()) @@ -547,8 +994,6 @@ OFCondition FGInterface::insertPerFrame(const Uint32 frameNo, FGBase* group, con { if (replaceExisting) { - DCMFG_DEBUG("Replacing per-frame FG for frame: " << frameNo << ", type: " - << DcmFGTypes::FGType2OFString(group->getType())); deletePerFrame(frameNo, group->getType()); } else @@ -604,6 +1049,40 @@ OFCondition FGInterface::convertSharedToPerFrame(const DcmFGTypes::E_FGType fgTy return result; } + +Uint32 FGInterface::findAdequateNumberOfThreads(const Uint32 numFrames, const Uint32 userThreadSetting) +{ + // If no threads are requested, we use one thread + if (userThreadSetting == 0) + { + DCMFG_DEBUG("No threads requested, using 1 thread by default."); + return 1; + } + // We should have at least 10 frames per thread, lower number of threads so + // that every thread has at least 10 frames to process. + Uint32 framesPerThread = numFrames / userThreadSetting; + if (framesPerThread < 10) + { + DCMFG_DEBUG("Requested number of threads (" << userThreadSetting + << ") is too high for the number of frames (" << numFrames + << "), reducing to a minimum of 10 frames per thread."); + // If the user requested more threads than we can use, we limit the number of threads + // to the number of frames divided by 10. + Uint32 numThreads = numFrames / 10; + if (numThreads < 1) + { + numThreads = 1; // At least one thread is required + } + return numThreads; + } + DCMFG_DEBUG("Using " << userThreadSetting << " threads for " << numFrames << " frames"); + + // If we have enough frames per thread, we can use the user setting + DCMFG_DEBUG("Using user-defined number of threads: " << userThreadSetting); + return userThreadSetting; +} + + FGBase* FGInterface::get(const Uint32 frameNo, const DcmFGTypes::E_FGType fgType, OFBool& isPerFrame) { FGBase* group = m_shared.find(fgType); @@ -628,7 +1107,7 @@ OFBool FGInterface::check() for (size_t frameCount = 0; frameCount < numFrames; frameCount++) { DCMFG_TRACE("Checking frame " << frameCount << "..."); - // Every frame requires the FrameContent functional group, check "en passant" + // Most IODS require the FrameContent functional group, check "en passant" OFBool foundFrameContent = OFFalse; OFMap::iterator frameFG = m_perFrame.begin(); OFMap::iterator end = m_perFrame.end(); @@ -665,8 +1144,9 @@ OFBool FGInterface::check() } if (!foundFrameContent) { - DCMFG_ERROR("Frame Content Functional group missing for frame #" << frameCount); - numErrors++; + // TODO: This is an error for some IODs but not all (it used to be in earlier editions of the standard) + DCMFG_WARN("Frame Content Functional group missing for frame #" << frameCount); + // numErrors++; } } diff --git a/dcmfg/libsrc/fgseg.cc b/dcmfg/libsrc/fgseg.cc index 71b44edd..d087b753 100644 --- a/dcmfg/libsrc/fgseg.cc +++ b/dcmfg/libsrc/fgseg.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2019, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -64,6 +64,12 @@ OFCondition FGSegmentation::getReferencedSegmentNumber(Uint16& value, const unsi OFCondition FGSegmentation::setReferencedSegmentNumber(const Uint16& segmentNumber) { + if (segmentNumber == 0) + { + // Segment numbers start at 1, so 0 is invalid + DCMFG_ERROR("Segment Number must not be 0"); + return FG_EC_InvalidData; + } return m_ReferencedSegmentNumber.putUint16(segmentNumber); } diff --git a/dcmfg/libsrc/fgtypes.cc b/dcmfg/libsrc/fgtypes.cc index 1a353668..762a189f 100644 --- a/dcmfg/libsrc/fgtypes.cc +++ b/dcmfg/libsrc/fgtypes.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -53,6 +53,7 @@ makeOFConditionConst(FG_EC_PixelDataTooLarge, OFM_dcmfg, 18, OF_error, "Pixel Da makeOFConditionConst(FG_EC_InconsistentConcatenationData, OFM_dcmfg, 19, OF_error, "Inconsistent Concatenation Data"); makeOFConditionConst(FG_EC_ConcatenationComplete, OFM_dcmfg, 20, OF_error, "Concatenation Complete - no more data"); makeOFConditionConst(FG_EC_UnsupportedPixelDataLayout, OFM_dcmfg, 21, OF_error, "Unsupported pixel data layout"); +makeOFConditionConst(FG_EC_ParallelProcessingFailed, OFM_dcmfg, 22, OF_error, "Parallel processing failed"); OFString DcmFGTypes::FGType2OFString(const DcmFGTypes::E_FGType fgType) { diff --git a/dcmfg/tests/CMakeLists.txt b/dcmfg/tests/CMakeLists.txt index 92b125b5..4e887eda 100644 --- a/dcmfg/tests/CMakeLists.txt +++ b/dcmfg/tests/CMakeLists.txt @@ -15,7 +15,7 @@ DCMTK_ADD_TEST_EXECUTABLE(dcmfg_tests ) # make sure executables are linked to the corresponding libraries -DCMTK_TARGET_LINK_MODULES(dcmfg_tests dcmfg dcmdata oflog ofstd) +DCMTK_TARGET_LINK_MODULES(dcmfg_tests dcmfg) # This macro parses tests.cc and registers all tests DCMTK_ADD_TESTS(dcmfg) diff --git a/dcmfg/tests/Makefile.dep b/dcmfg/tests/Makefile.dep index 7e1d85a9..bceb2951 100644 --- a/dcmfg/tests/Makefile.dep +++ b/dcmfg/tests/Makefile.dep @@ -59,7 +59,11 @@ t_concatenation_creator.o: t_concatenation_creator.cc \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ - ../include/dcmtk/dcmfg/fgdefine.h ../include/dcmtk/dcmfg/fgtypes.h \ + ../include/dcmtk/dcmfg/fgdefine.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../include/dcmtk/dcmfg/fgtypes.h \ ../../ofstd/include/dcmtk/ofstd/oftempf.h \ ../../ofstd/include/dcmtk/ofstd/oftest.h \ ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ @@ -115,6 +119,9 @@ t_concatenation_loader.o: t_concatenation_loader.cc \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/offile.h \ ../../ofstd/include/dcmtk/ofstd/ofstd.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ @@ -125,11 +132,9 @@ t_concatenation_loader.o: t_concatenation_loader.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ @@ -537,40 +542,30 @@ t_ct_table_dynamics.o: t_ct_table_dynamics.cc \ t_deriv_image.o: t_deriv_image.cc \ ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmfg/fgderimg.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ - ../../ofstd/include/dcmtk/ofstd/offile.h \ - ../../ofstd/include/dcmtk/ofstd/oftypes.h \ - ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ - ../../ofstd/include/dcmtk/ofstd/ofcast.h \ - ../../ofstd/include/dcmtk/ofstd/ofexport.h \ - ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ - ../../ofstd/include/dcmtk/ofstd/ofstring.h \ - ../../ofstd/include/dcmtk/ofstd/ofstream.h \ - ../../ofstd/include/dcmtk/ofstd/ofstd.h \ - ../../ofstd/include/dcmtk/ofstd/oflist.h \ - ../../ofstd/include/dcmtk/ofstd/oftraits.h \ - ../../ofstd/include/dcmtk/ofstd/ofcond.h \ - ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ - ../../ofstd/include/dcmtk/ofstd/diag/push.def \ - ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ - ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ - ../../ofstd/include/dcmtk/ofstd/oflimits.h \ - ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ ../../oflog/include/dcmtk/oflog/oflog.h \ ../../oflog/include/dcmtk/oflog/logger.h \ ../../oflog/include/dcmtk/oflog/config.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ ../../oflog/include/dcmtk/oflog/config/defines.h \ ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ ../../oflog/include/dcmtk/oflog/loglevel.h \ ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ ../../oflog/include/dcmtk/oflog/tchar.h \ ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ ../../oflog/include/dcmtk/oflog/appender.h \ ../../ofstd/include/dcmtk/ofstd/ofmem.h \ ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ ../../oflog/include/dcmtk/oflog/layout.h \ ../../oflog/include/dcmtk/oflog/streams.h \ @@ -583,10 +578,16 @@ t_deriv_image.o: t_deriv_image.cc \ ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ @@ -594,24 +595,28 @@ t_deriv_image.o: t_deriv_image.cc \ ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ + ../include/dcmtk/dcmfg/fgbase.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ - ../include/dcmtk/dcmfg/fgbase.h ../include/dcmtk/dcmfg/fgtypes.h \ - ../include/dcmtk/dcmfg/fgdefine.h \ + ../include/dcmtk/dcmfg/fgtypes.h ../include/dcmtk/dcmfg/fgdefine.h \ ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrst.h \ diff --git a/dcmfg/tests/t_concatenation_loader.cc b/dcmfg/tests/t_concatenation_loader.cc index 72926132..495407a8 100644 --- a/dcmfg/tests/t_concatenation_loader.cc +++ b/dcmfg/tests/t_concatenation_loader.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019-2024, OFFIS e.V. + * Copyright (C) 2019-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -93,7 +93,7 @@ OFTEST(dcmfg_concatenation_loader) // Scanning done, now load DcmFileFormat dcmff; - OFVector frames; + OFVector frames; result = cl.load("1.3.6.1.4.1.5962.1.7.70.2.1.1166562673.14401", dcmff.getDataset(), frames); OFCHECK(result.good()); if (result.good()) @@ -402,7 +402,7 @@ static void prepare_dset_dump() DSET_DUMP += " (0018,1210) SH [STANDARD] # 8, 1 ConvolutionKernel\n"; DSET_DUMP += " (0018,9315) CS [ITERATIVE] # 10, 1 ReconstructionAlgorithm\n"; DSET_DUMP += " (0018,9316) CS [SOFT_TISSUE] # 12, 1 ConvolutionKernelGroup\n"; - DSET_DUMP += " (0018,9319) FD 360 # 8, 1 ReconstructionAngle\n"; + DSET_DUMP += " (0018,9319) FD 360.00024414062494 # 8, 1 ReconstructionAngle\n"; DSET_DUMP += " (0018,9320) SH [None] # 4, 1 ImageFilter\n"; DSET_DUMP += " (0018,9322) FD 0.6347661018371581\\0.6347661018371581 # 16, 2 ReconstructionPixelSpacing\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; @@ -410,9 +410,9 @@ static void prepare_dset_dump() DSET_DUMP += " (0018,9321) SQ (Sequence with undefined length #=1) # u/l, 1 CTExposureSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=5) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9323) CS [NONE] # 4, 1 ExposureModulationType\n"; - DSET_DUMP += " (0018,9328) FD 1000 # 8, 1 ExposureTimeInms\n"; - DSET_DUMP += " (0018,9330) FD 230 # 8, 1 XRayTubeCurrentInmA\n"; - DSET_DUMP += " (0018,9332) FD 230 # 8, 1 ExposureInmAs\n"; + DSET_DUMP += " (0018,9328) FD 1000.0004882812499 # 8, 1 ExposureTimeInms\n"; + DSET_DUMP += " (0018,9330) FD 230.00012207031247 # 8, 1 XRayTubeCurrentInmA\n"; + DSET_DUMP += " (0018,9332) FD 230.00012207031247 # 8, 1 ExposureInmAs\n"; DSET_DUMP += " (0018,9345) FD (no value available) # 0, 0 CTDIvol\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -479,8 +479,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-10.00000762939453 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-10.00000762939453 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-10.00000762939453 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-10.00000762939453 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -10.00000762939453 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -517,8 +517,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-17.00001525878906 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-17.00001525878906 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-17.00001525878906 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-17.00001525878906 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -17.00143432617187 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -555,9 +555,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-24.00001525878906 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-24.00001525878906 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -24.0029 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-24.00001525878906 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-24.00001525878906 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -24.002853393554684 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -593,9 +593,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-31.00001525878906 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-31.00001525878906 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -31.0043 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-31.00001525878906 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-31.00001525878906 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -31.004272460937496 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -631,8 +631,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-38.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-38.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-38.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-38.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -37.99575805664062 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -669,8 +669,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-45.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-45.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-45.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-45.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -44.99716186523437 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -707,8 +707,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-52.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-52.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-52.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-52.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -51.99859619140624 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -745,8 +745,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-59.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-59.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-59.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-59.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -59.00003051757812 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -783,8 +783,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-66.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-66.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-66.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-66.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -66.00146484374999 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -821,8 +821,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-73.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-73.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-73.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-73.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -73.00286865234374 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -859,8 +859,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-80.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-80.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-80.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-80.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -80.00433349609374 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -897,8 +897,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-87.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-87.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-87.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-87.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -86.99578857421874 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -935,8 +935,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-94.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-94.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-94.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-94.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -93.99719238281249 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -973,9 +973,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-101 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-101 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -100.999 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-101.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-101.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -100.99859619140624 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1011,9 +1011,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-108 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-108 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -108 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-108.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-108.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -108.00006103515624 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1049,9 +1049,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-115 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-115 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -115.001 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-115.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-115.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -115.00146484374999 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1087,9 +1087,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-122 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-122 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -122.003 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-122.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-122.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -122.00286865234374 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1125,9 +1125,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-129 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-129 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -129.004 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-129.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-129.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -129.00439453124997 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1163,9 +1163,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-136 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-136 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -135.996 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-136.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-136.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -135.99584960937497 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1201,9 +1201,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-143 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-143 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -142.997 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-143.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-143.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -142.99719238281247 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1239,9 +1239,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-150 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-150 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -149.999 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-150.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-150.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -149.99865722656247 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1277,9 +1277,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-157 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-157 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -157 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-157.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-157.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -157.00012207031247 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1315,9 +1315,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-164 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-164 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -164.001 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-164.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-164.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -164.00146484374997 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1353,9 +1353,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-171 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-171 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -171.003 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-171.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-171.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -171.00292968749997 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1391,9 +1391,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-178 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-178 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -178.004 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-178.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-178.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -178.00439453124997 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1429,9 +1429,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-185 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-185 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -184.996 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-185.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-185.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -184.99584960937497 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1467,9 +1467,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-192 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-192 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -191.997 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-192.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-192.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -191.99719238281247 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1505,9 +1505,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-199 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-199 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -198.999 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-199.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-199.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -198.99865722656247 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1543,9 +1543,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-206 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-206 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -206 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-206.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-206.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -206.00012207031247 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1581,9 +1581,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-213 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-213 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -213.001 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-213.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-213.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -213.00146484374997 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1619,8 +1619,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-10.00000762939453 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-10.00000762939453 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-10.00000762939453 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-10.00000762939453 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -10.00000762939453 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -1657,8 +1657,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-17.00001525878906 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-17.00001525878906 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-17.00001525878906 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-17.00001525878906 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -17.00143432617187 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -1695,9 +1695,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-24.00001525878906 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-24.00001525878906 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -24.0029 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-24.00001525878906 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-24.00001525878906 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -24.002853393554684 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1733,9 +1733,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-31.00001525878906 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-31.00001525878906 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -31.0043 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-31.00001525878906 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-31.00001525878906 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -31.004272460937496 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -1771,8 +1771,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-38.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-38.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-38.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-38.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -37.99575805664062 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -1809,8 +1809,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-45.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-45.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-45.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-45.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -44.99716186523437 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -1847,8 +1847,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-52.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-52.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-52.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-52.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -51.99859619140624 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -1885,8 +1885,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-59.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-59.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-59.00003051757812 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-59.00003051757812 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -59.00003051757812 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -1923,8 +1923,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-66.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-66.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-66.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-66.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -66.00146484374999 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -1961,8 +1961,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-73.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-73.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-73.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-73.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -73.00286865234374 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -1999,8 +1999,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-80.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-80.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-80.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-80.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -80.00433349609374 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -2037,8 +2037,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-87.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-87.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-87.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-87.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -86.99578857421874 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -2075,8 +2075,8 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-94.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-94.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-94.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-94.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; DSET_DUMP += " (0018,9327) FD -93.99719238281249 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; @@ -2113,9 +2113,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-101 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-101 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -100.999 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-101.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-101.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -100.99859619140624 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2151,9 +2151,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-108 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-108 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -108 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-108.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-108.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -108.00006103515624 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2189,9 +2189,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-115 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-115 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -115.001 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-115.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-115.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -115.00146484374999 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2227,9 +2227,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-122 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-122 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -122.003 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-122.00006103515624 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-122.00006103515624 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -122.00286865234374 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2265,9 +2265,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-129 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-129 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -129.004 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-129.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-129.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -129.00439453124997 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2303,9 +2303,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-136 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-136 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -135.996 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-136.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-136.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -135.99584960937497 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2341,9 +2341,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-143 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-143 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -142.997 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-143.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-143.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -142.99719238281247 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2379,9 +2379,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-150 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-150 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -149.999 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-150.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-150.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -149.99865722656247 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2417,9 +2417,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-157 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-157 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -157 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-157.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-157.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -157.00012207031247 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2455,9 +2455,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-164 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-164 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -164.001 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-164.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-164.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -164.00146484374997 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2493,9 +2493,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-171 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-171 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -171.003 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-171.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-171.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -171.00292968749997 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2531,9 +2531,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-178 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-178 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -178.004 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-178.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-178.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -178.00439453124997 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2569,9 +2569,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-185 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-185 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -184.996 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-185.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-185.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -184.99584960937497 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2607,9 +2607,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-192 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-192 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -191.997 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-192.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-192.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -191.99719238281247 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2645,9 +2645,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-199 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-199 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -198.999 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-199.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-199.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -198.99865722656247 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2683,9 +2683,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-206 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-206 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -206 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-206.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-206.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -206.00012207031247 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; @@ -2721,9 +2721,9 @@ static void prepare_dset_dump() DSET_DUMP += " (fffe,e000) na (Item with undefined length #=4) # u/l, 1 Item\n"; DSET_DUMP += " (0018,9326) SQ (Sequence with undefined length #=1) # u/l, 1 CTPositionSequence\n"; DSET_DUMP += " (fffe,e000) na (Item with undefined length #=3) # u/l, 1 Item\n"; - DSET_DUMP += " (0018,9313) FD 3.08271\\-0.3172872066497802\\-213 # 24, 3 DataCollectionCenterPatient\n"; - DSET_DUMP += " (0018,9318) FD 3.08271\\-0.3172872066497802\\-213 # 24, 3 ReconstructionTargetCenterPatient\n"; - DSET_DUMP += " (0018,9327) FD -213.001 # 8, 1 TablePosition\n"; + DSET_DUMP += " (0018,9313) FD 3.0827140808105464\\-0.3172872066497802\\-213.00012207031247 # 24, 3 DataCollectionCenterPatient\n"; + DSET_DUMP += " (0018,9318) FD 3.0827140808105464\\-0.3172872066497802\\-213.00012207031247 # 24, 3 ReconstructionTargetCenterPatient\n"; + DSET_DUMP += " (0018,9327) FD -213.00146484374997 # 8, 1 TablePosition\n"; DSET_DUMP += " (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem\n"; DSET_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem\n"; DSET_DUMP += " (0018,9341) SQ (Sequence with undefined length #=2) # u/l, 1 ContrastBolusUsageSequence\n"; diff --git a/dcmfg/tests/t_fg_base.cc b/dcmfg/tests/t_fg_base.cc index 00437a68..faec8103 100644 --- a/dcmfg/tests/t_fg_base.cc +++ b/dcmfg/tests/t_fg_base.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2022-2024, OFFIS e.V. + * Copyright (C) 2022-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -46,4 +46,5 @@ OFTEST(dcmfg_fgbase_fgunknown) FGUnknown* fg_copy = OFstatic_cast(FGUnknown*, fg.clone()); OFCHECK(fg_copy != NULL); OFCHECK(fg.compare(*fg_copy) == 0); + delete fg_copy; } diff --git a/dcmimage/docs/dcm2pnm.man b/dcmimage/docs/dcm2pnm.man index c6238c2e..50d6e09a 100644 --- a/dcmimage/docs/dcm2pnm.man +++ b/dcmimage/docs/dcm2pnm.man @@ -23,6 +23,6 @@ the same command line parameters, and more. \section dcm2pnm_copyright COPYRIGHT -Copyright (C) 1998-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1998-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmimage/docs/dcmicmp.man b/dcmimage/docs/dcmicmp.man index e9c56d83..51ca8208 100644 --- a/dcmimage/docs/dcmicmp.man +++ b/dcmimage/docs/dcmicmp.man @@ -372,6 +372,6 @@ It is an error if no data dictionary can be loaded. \section dcmicmp_copyright COPYRIGHT -Copyright (C) 2018-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2018-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmimage/docs/dcmquant.man b/dcmimage/docs/dcmquant.man index 0bc03eb3..f77ffbd2 100644 --- a/dcmimage/docs/dcmquant.man +++ b/dcmimage/docs/dcmquant.man @@ -284,6 +284,6 @@ It is an error if no data dictionary can be loaded. \section dcmquant_copyright COPYRIGHT -Copyright (C) 2001-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2001-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmimage/docs/dcmscale.man b/dcmimage/docs/dcmscale.man index 404802df..15e28034 100644 --- a/dcmimage/docs/dcmscale.man +++ b/dcmimage/docs/dcmscale.man @@ -266,6 +266,6 @@ It is an error if no data dictionary can be loaded. \section dcmscale_copyright COPYRIGHT -Copyright (C) 2002-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2002-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmimage/include/dcmtk/dcmimage/dicomot.h b/dcmimage/include/dcmtk/dcmimage/dicomot.h index 0a11f290..450bc11e 100644 --- a/dcmimage/include/dcmtk/dcmimage/dicomot.h +++ b/dcmimage/include/dcmtk/dcmimage/dicomot.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2016, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -88,7 +88,7 @@ class DiColorMonoTemplate { if (pixel != NULL) { - this->Data = new T[this->Count]; + this->Data = new (std::nothrow) T[this->Count]; if (this->Data != NULL) { const T *r = pixel[0]; diff --git a/dcmimage/include/dcmtk/dcmimage/dicopxt.h b/dcmimage/include/dcmtk/dcmimage/dicopxt.h index d812d169..0ece5329 100644 --- a/dcmimage/include/dcmtk/dcmimage/dicopxt.h +++ b/dcmimage/include/dcmtk/dcmimage/dicopxt.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2016, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -125,16 +125,9 @@ class DiColorPixelTemplate */ virtual ~DiColorPixelTemplate() { -#if defined(HAVE_STD__NOTHROW) && defined(HAVE_NOTHROW_DELETE) - /* use a non-throwing delete (if available) */ operator delete[] (Data[0], std::nothrow); operator delete[] (Data[1], std::nothrow); operator delete[] (Data[2], std::nothrow); -#else - delete[] Data[0]; - delete[] Data[1]; - delete[] Data[2]; -#endif } /** get integer representation @@ -365,7 +358,7 @@ class DiColorPixelTemplate data = new Uint32[count]; if (data != NULL) { - Uint32 *q = OFstatic_cast(Uint32 *, data); + Uint8 *q = OFstatic_cast(Uint8 *, data); if (fromBits == toBits) { /* copy pixel data as is */ @@ -373,10 +366,11 @@ class DiColorPixelTemplate { for (x = width; x != 0; x--) { - /* normal sample order: 0-R-G-B */ - *(q++) = (OFstatic_cast(Uint32, *(r++)) << 16) | - (OFstatic_cast(Uint32, *(g++)) << 8) | - OFstatic_cast(Uint32, *(b++)); + /* sample order: B-G-R-0 */ + *(q++) = OFstatic_cast(Uint8, *(b++)); + *(q++) = OFstatic_cast(Uint8, *(g++)); + *(q++) = OFstatic_cast(Uint8, *(r++)); + *(q++) = 0; } r += nextRow; g += nextRow; b += nextRow; // go backwards if 'upsideDown' } @@ -393,10 +387,11 @@ class DiColorPixelTemplate { for (x = width; x != 0; x--) { - /* normal sample order: 0-R-G-B */ - *(q++) = (OFstatic_cast(Uint32, *(r++) * gradient2) << 16) | - (OFstatic_cast(Uint32, *(g++) * gradient2) << 8) | - OFstatic_cast(Uint32, *(b++) * gradient2); + /* sample order: B-G-R-0 */ + *(q++) = OFstatic_cast(Uint8, *(b++) * gradient2); + *(q++) = OFstatic_cast(Uint8, *(g++) * gradient2); + *(q++) = OFstatic_cast(Uint8, *(r++) * gradient2); + *(q++) = 0; } r += nextRow; g += nextRow; b += nextRow; // go backwards if 'upsideDown' } @@ -405,10 +400,11 @@ class DiColorPixelTemplate { for (x = width; x != 0; x--) { - /* normal sample order: 0-R-G-B */ - *(q++) = (OFstatic_cast(Uint32, OFstatic_cast(double, *(r++)) * gradient1) << 16) | - (OFstatic_cast(Uint32, OFstatic_cast(double, *(g++)) * gradient1) << 8) | - OFstatic_cast(Uint32, OFstatic_cast(double, *(b++)) * gradient1); + /* sample order: B-G-R-0 */ + *(q++) = OFstatic_cast(Uint8, OFstatic_cast(double, *(b++)) * gradient1); + *(q++) = OFstatic_cast(Uint8, OFstatic_cast(double, *(g++)) * gradient1); + *(q++) = OFstatic_cast(Uint8, OFstatic_cast(double, *(r++)) * gradient1); + *(q++) = 0; } r += nextRow; g += nextRow; b += nextRow; // go backwards if 'upsideDown' } @@ -422,10 +418,11 @@ class DiColorPixelTemplate { for (x = width; x != 0; x--) { - /* normal sample order: 0-R-G-B */ - *(q++) = (OFstatic_cast(Uint32, *(r++) >> shift) << 16) | - (OFstatic_cast(Uint32, *(g++) >> shift) << 8) | - OFstatic_cast(Uint32, *(b++) >> shift); + /* sample order: B-G-R-0 */ + *(q++) = OFstatic_cast(Uint8, *(b++) >> shift); + *(q++) = OFstatic_cast(Uint8, *(g++) >> shift); + *(q++) = OFstatic_cast(Uint8, *(r++) >> shift); + *(q++) = 0; } r += nextRow; g += nextRow; b += nextRow; // go backwards if 'upsideDown' } @@ -556,25 +553,17 @@ class DiColorPixelTemplate /* allocate data buffer for the 3 planes */ for (int j = 0; j < 3; j++) { -#ifdef HAVE_STD__NOTHROW /* use a non-throwing new here (if available) because the allocated buffer can be huge */ Data[j] = new (std::nothrow) T[Count]; -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - Data[j] = new T[Count]; - } - catch (STD_NAMESPACE bad_alloc const &) - { - Data[j] = NULL; - } -#endif if (Data[j] != NULL) { /* erase empty part of the buffer (=blacken the background) */ if (InputCount < Count) - OFBitmanipTemplate::zeroMem(Data[j] + InputCount, Count - InputCount); + { + const size_t count = (Count - InputCount); + DCMIMAGE_TRACE("filing empty part of the intermediate pixel data (" << count << " pixels) of plane " << j << " with value = 0"); + OFBitmanipTemplate::zeroMem(Data[j] + InputCount, count); + } } else { DCMIMAGE_DEBUG("cannot allocate memory buffer for 'Data[" << j << "]' in DiColorPixelTemplate::Init()"); result = 0; // at least one buffer could not be allocated! diff --git a/dcmimage/include/dcmtk/dcmimage/diqtpbox.h b/dcmimage/include/dcmtk/dcmimage/diqtpbox.h index e787ab24..c7b13b1f 100644 --- a/dcmimage/include/dcmtk/dcmimage/diqtpbox.h +++ b/dcmimage/include/dcmtk/dcmimage/diqtpbox.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2021, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -31,10 +31,8 @@ #include "dcmtk/dcmimage/dicdefin.h" BEGIN_EXTERN_C -#ifdef HAVE_SYS_TYPES_H /* needed e.g. on Solaris for definition of size_t */ #include -#endif END_EXTERN_C /** helper structure for class DcmQuantPixelBoxArray. diff --git a/dcmimage/include/dcmtk/dcmimage/diybrpxt.h b/dcmimage/include/dcmtk/dcmimage/diybrpxt.h index 96557297..3fcb7e28 100644 --- a/dcmimage/include/dcmtk/dcmimage/diybrpxt.h +++ b/dcmimage/include/dcmtk/dcmimage/diybrpxt.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1998-2016, OFFIS e.V. + * Copyright (C) 1998-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -24,6 +24,7 @@ #define DIYBRPXT_H #include "dcmtk/config/osconfig.h" +#include "dcmtk/ofstd/ofbmanip.h" #include "dcmtk/dcmimage/dicopxt.h" #include "dcmtk/dcmimgle/diinpx.h" /* gcc 3.4 needs this */ @@ -90,179 +91,190 @@ class DiYBRPixelTemplate // use the number of input pixels derived from the length of the 'PixelData' // attribute), but not more than the size of the intermediate buffer const unsigned long count = (this->InputCount < this->Count) ? this->InputCount : this->Count; - if (rgb) /* convert to RGB model */ + // make sure that there is sufficient input data (for planar pixel data) + if (!this->PlanarConfiguration || (count >= planeSize)) { - T2 *r = this->Data[0]; - T2 *g = this->Data[1]; - T2 *b = this->Data[2]; - const T2 maxvalue = OFstatic_cast(T2, DicomImageClass::maxval(bits)); - DiPixelRepresentationTemplate rep; - if (bits == 8 && !rep.isSigned()) // only for unsigned 8 bit + if (rgb) /* convert to RGB model */ { - Sint16 rcr_tab[256]; - Sint16 gcb_tab[256]; - Sint16 gcr_tab[256]; - Sint16 bcb_tab[256]; - const double r_const = 0.7010 * OFstatic_cast(double, maxvalue); - const double g_const = 0.5291 * OFstatic_cast(double, maxvalue); - const double b_const = 0.8859 * OFstatic_cast(double, maxvalue); - unsigned long l; - for (l = 0; l < 256; ++l) + T2 *r = this->Data[0]; + T2 *g = this->Data[1]; + T2 *b = this->Data[2]; + const T2 maxvalue = OFstatic_cast(T2, DicomImageClass::maxval(bits)); + DiPixelRepresentationTemplate rep; + if (bits == 8 && !rep.isSigned()) // only for unsigned 8 bit { - rcr_tab[l] = OFstatic_cast(Sint16, 1.4020 * OFstatic_cast(double, l) - r_const); - gcb_tab[l] = OFstatic_cast(Sint16, 0.3441 * OFstatic_cast(double, l)); - gcr_tab[l] = OFstatic_cast(Sint16, 0.7141 * OFstatic_cast(double, l) - g_const); - bcb_tab[l] = OFstatic_cast(Sint16, 1.7720 * OFstatic_cast(double, l) - b_const); - } - Sint32 sr; - Sint32 sg; - Sint32 sb; - if (this->PlanarConfiguration) - { -/* - const T1 *y = pixel; - const T1 *cb = y + this->InputCount; - const T1 *cr = cb + this->InputCount; - for (i = count; i != 0; --i, ++y, ++cb, ++cr) + Sint16 rcr_tab[256]; + Sint16 gcb_tab[256]; + Sint16 gcr_tab[256]; + Sint16 bcb_tab[256]; + const double r_const = 0.7010 * OFstatic_cast(double, maxvalue); + const double g_const = 0.5291 * OFstatic_cast(double, maxvalue); + const double b_const = 0.8859 * OFstatic_cast(double, maxvalue); + unsigned long l; + for (l = 0; l < 256; ++l) { - sr = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, rcr_tab[*cr]); - sg = OFstatic_cast(Sint32, *y) - OFstatic_cast(Sint32, gcb_tab[*cb]) - OFstatic_cast(Sint32, gcr_tab[*cr]); - sb = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, bcb_tab[*cb]); - *(r++) = (sr < 0) ? 0 : (sr > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sr); - *(g++) = (sg < 0) ? 0 : (sg > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sg); - *(b++) = (sb < 0) ? 0 : (sb > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sb); + rcr_tab[l] = OFstatic_cast(Sint16, 1.4020 * OFstatic_cast(double, l) - r_const); + gcb_tab[l] = OFstatic_cast(Sint16, 0.3441 * OFstatic_cast(double, l)); + gcr_tab[l] = OFstatic_cast(Sint16, 0.7141 * OFstatic_cast(double, l) - g_const); + bcb_tab[l] = OFstatic_cast(Sint16, 1.7720 * OFstatic_cast(double, l) - b_const); } + Sint32 sr; + Sint32 sg; + Sint32 sb; + if (this->PlanarConfiguration) + { +/* + const T1 *y = pixel; + const T1 *cb = y + this->InputCount; + const T1 *cr = cb + this->InputCount; + for (i = count; i != 0; --i, ++y, ++cb, ++cr) + { + sr = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, rcr_tab[*cr]); + sg = OFstatic_cast(Sint32, *y) - OFstatic_cast(Sint32, gcb_tab[*cb]) - OFstatic_cast(Sint32, gcr_tab[*cr]); + sb = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, bcb_tab[*cb]); + *(r++) = (sr < 0) ? 0 : (sr > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sr); + *(g++) = (sg < 0) ? 0 : (sg > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sg); + *(b++) = (sb < 0) ? 0 : (sb > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sb); + } */ - const T1 *y = pixel; - const T1 *cb = y + planeSize; - const T1 *cr = cb + planeSize; - unsigned long i = count; - while (i != 0) + const T1 *y = pixel; + const T1 *cb = y + planeSize; + const T1 *cr = cb + planeSize; + unsigned long i = count; + while (i != 0) + { + /* convert a single frame */ + for (l = planeSize; (l != 0) && (i != 0); --l, --i, ++y, ++cb, ++cr) + { + const Sint32 yValue = *y; + /* conversion to unsigned integer needed for gcc 14 on Solaris */ + const unsigned int cbValue = *cb; + const unsigned int crValue = *cr; + sr = yValue + rcr_tab[crValue]; + sg = yValue - gcb_tab[cbValue] - gcr_tab[crValue]; + sb = yValue + bcb_tab[cbValue]; + *(r++) = (sr < 0) ? 0 : (sr > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sr); + *(g++) = (sg < 0) ? 0 : (sg > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sg); + *(b++) = (sb < 0) ? 0 : (sb > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sb); + } + /* jump to next frame start (skip 2 planes) */ + y += 2 * planeSize; + cb += 2 * planeSize; + cr += 2 * planeSize; + } + } + else { - /* convert a single frame */ - for (l = planeSize; (l != 0) && (i != 0); --l, --i, ++y, ++cb, ++cr) + const T1 *p = pixel; + unsigned long i; + for (i = count; i != 0; --i) { - sr = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, rcr_tab[OFstatic_cast(Uint32, *cr)]); - sg = OFstatic_cast(Sint32, *y) - OFstatic_cast(Sint32, gcb_tab[OFstatic_cast(Uint32, *cb)]) - OFstatic_cast(Sint32, gcr_tab[OFstatic_cast(Uint32, *cr)]); - sb = OFstatic_cast(Sint32, *y) + OFstatic_cast(Sint32, bcb_tab[OFstatic_cast(Uint32, *cb)]); + const Sint32 yValue = *(p++); + /* conversion to unsigned integer needed for gcc 14 on Solaris */ + const unsigned int cbValue = *(p++); + const unsigned int crValue = *(p++); + sr = yValue + rcr_tab[crValue]; + sg = yValue - gcb_tab[cbValue] - gcr_tab[crValue]; + sb = yValue + bcb_tab[cbValue]; *(r++) = (sr < 0) ? 0 : (sr > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sr); *(g++) = (sg < 0) ? 0 : (sg > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sg); *(b++) = (sb < 0) ? 0 : (sb > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sb); } - /* jump to next frame start (skip 2 planes) */ - y += 2 * planeSize; - cb += 2 * planeSize; - cr += 2 * planeSize; } } else { - const T1 *p = pixel; - T1 y; - T1 cb; - T1 cr; - unsigned long i; - for (i = count; i != 0; --i) + if (this->PlanarConfiguration) + { +/* + const T1 *y = pixel; + const T1 *cb = y + this->InputCount; + const T1 *cr = cb + this->InputCount; + for (i = count; i != 0; --i) + convertValue(*(r++), *(g++), *(b++), removeSign(*(y++), offset), removeSign(*(cb++), offset), + removeSign(*(cr++), offset), maxvalue); +*/ + unsigned long l; + unsigned long i = count; + const T1 *y = pixel; + const T1 *cb = y + planeSize; + const T1 *cr = cb + planeSize; + while (i != 0) + { + /* convert a single frame */ + for (l = planeSize; (l != 0) && (i != 0); --l, --i) + { + const T2 yValue = removeSign(*(y++), offset); + const T2 cbValue = removeSign(*(cb++), offset); + const T2 crValue = removeSign(*(cr++), offset); + convertValue(*(r++), *(g++), *(b++), yValue, cbValue, crValue, maxvalue); + } + /* jump to next frame start (skip 2 planes) */ + y += 2 * planeSize; + cb += 2 * planeSize; + cr += 2 * planeSize; + } + } + else { - y = *(p++); - cb = *(p++); - cr = *(p++); - sr = OFstatic_cast(Sint32, y) + OFstatic_cast(Sint32, rcr_tab[OFstatic_cast(Uint32, cr)]); - sg = OFstatic_cast(Sint32, y) - OFstatic_cast(Sint32, gcb_tab[OFstatic_cast(Uint32, cb)]) - OFstatic_cast(Sint32, gcr_tab[OFstatic_cast(Uint32, cr)]); - sb = OFstatic_cast(Sint32, y) + OFstatic_cast(Sint32, bcb_tab[OFstatic_cast(Uint32, cb)]); - *(r++) = (sr < 0) ? 0 : (sr > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sr); - *(g++) = (sg < 0) ? 0 : (sg > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sg); - *(b++) = (sb < 0) ? 0 : (sb > OFstatic_cast(Sint32, maxvalue)) ? maxvalue : OFstatic_cast(T2, sb); + const T1 *p = pixel; + unsigned long i; + for (i = count; i != 0; --i) + { + const T2 yValue = removeSign(*(p++), offset); + const T2 cbValue = removeSign(*(p++), offset); + const T2 crValue = removeSign(*(p++), offset); + convertValue(*(r++), *(g++), *(b++), yValue, cbValue, crValue, maxvalue); + } } } - } - else - { + } else { /* retain YCbCr model */ + const T1 *p = pixel; if (this->PlanarConfiguration) { /* - const T1 *y = pixel; - const T1 *cb = y + this->InputCount; - const T1 *cr = cb + this->InputCount; - for (i = count; i != 0; --i) - convertValue(*(r++), *(g++), *(b++), removeSign(*(y++), offset), removeSign(*(cb++), offset), - removeSign(*(cr++), offset), maxvalue); + T2 *q; + // number of pixels to be skipped (only applicable if 'PixelData' contains more + // pixels than expected) + const unsigned long skip = (this->InputCount > this->Count) ? (this->InputCount - this->Count) : 0; + for (int j = 0; j < 3; ++j) + { + q = this->Data[j]; + for (i = count; i != 0; --i) + *(q++) = removeSign(*(p++), offset); + // skip to beginning of next plane + p += skip; + } */ unsigned long l; - unsigned long i = count; - const T1 *y = pixel; - const T1 *cb = y + planeSize; - const T1 *cr = cb + planeSize; - while (i != 0) + unsigned long i = 0; + while (i < count) { - /* convert a single frame */ - for (l = planeSize; (l != 0) && (i != 0); --l, --i) + /* store current pixel index */ + const unsigned long iStart = i; + for (int j = 0; j < 3; ++j) { - convertValue(*(r++), *(g++), *(b++), removeSign(*(y++), offset), removeSign(*(cb++), offset), - removeSign(*(cr++), offset), maxvalue); + /* convert a single plane */ + for (l = planeSize, i = iStart; (l != 0) && (i < count); --l, ++i) + this->Data[j][i] = removeSign(*(p++), offset); } - /* jump to next frame start (skip 2 planes) */ - y += 2 * planeSize; - cb += 2 * planeSize; - cr += 2 * planeSize; } } else { - const T1 *p = pixel; - T2 y; - T2 cb; - T2 cr; + int j; unsigned long i; - for (i = count; i != 0; --i) - { - y = removeSign(*(p++), offset); - cb = removeSign(*(p++), offset); - cr = removeSign(*(p++), offset); - convertValue(*(r++), *(g++), *(b++), y, cb, cr, maxvalue); - } - } - } - } else { /* retain YCbCr model */ - const T1 *p = pixel; - if (this->PlanarConfiguration) - { -/* - T2 *q; - // number of pixels to be skipped (only applicable if 'PixelData' contains more - // pixels than expected) - const unsigned long skip = (this->InputCount > this->Count) ? (this->InputCount - this->Count) : 0; - for (int j = 0; j < 3; ++j) - { - q = this->Data[j]; - for (i = count; i != 0; --i) - *(q++) = removeSign(*(p++), offset); - // skip to beginning of next plane - p += skip; - } -*/ - unsigned long l; - unsigned long i = 0; - while (i < count) - { - /* store current pixel index */ - const unsigned long iStart = i; - for (int j = 0; j < 3; ++j) - { - /* convert a single plane */ - for (l = planeSize, i = iStart; (l != 0) && (i < count); --l, ++i) - this->Data[j][i] = removeSign(*(p++), offset); - } + for (i = 0; i < count; ++i) /* for all pixel ... */ + for (j = 0; j < 3; ++j) + this->Data[j][i] = removeSign(*(p++), offset); /* ... copy planes */ } } - else - { - int j; - unsigned long i; - for (i = 0; i < count; ++i) /* for all pixel ... */ - for (j = 0; j < 3; ++j) - this->Data[j][i] = removeSign(*(p++), offset); /* ... copy planes */ - } + } else { + // do not process the input data, as it is too short + DCMIMAGE_WARN("input data is too short, filling the complete image with black pixels"); + // erase empty part of the buffer (that has not been "blackened" yet) + for (int j = 0; j < 3; ++j) + OFBitmanipTemplate::zeroMem(this->Data[j], count); } } } diff --git a/dcmimage/libsrc/Makefile.dep b/dcmimage/libsrc/Makefile.dep index 5cd88f55..fb169f06 100644 --- a/dcmimage/libsrc/Makefile.dep +++ b/dcmimage/libsrc/Makefile.dep @@ -223,13 +223,14 @@ diargimg.o: diargimg.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/restrict.def \ ../../dcmimgle/include/dcmtk/dcmimgle/dipxrept.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dibaslut.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diinpx.h \ ../include/dcmtk/dcmimage/diqttype.h \ ../../dcmimgle/include/dcmtk/dcmimgle/didocu.h \ ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h dicmyimg.o: dicmyimg.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ @@ -495,9 +496,21 @@ dicopx.o: dicopx.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimopx.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimomod.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dibaslut.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diobjcou.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diinpx.h \ ../../dcmimgle/include/dcmtk/dcmimgle/didocu.h \ ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ @@ -505,19 +518,8 @@ dicopx.o: dicopx.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/offile.h \ ../../ofstd/include/dcmtk/ofstd/ofstd.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ - ../../ofstd/include/dcmtk/ofstd/ofcond.h \ - ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ - ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ - ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ - ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ @@ -719,6 +721,7 @@ dipalimg.o: dipalimg.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/restrict.def \ ../../dcmimgle/include/dcmtk/dcmimgle/dipxrept.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dibaslut.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diinpx.h \ ../include/dcmtk/dcmimage/diqttype.h @@ -1043,6 +1046,8 @@ diqthash.o: diqthash.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dipixel.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimomod.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dibaslut.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimoopx.h \ ../../dcmimgle/include/dcmtk/dcmimgle/didispfn.h @@ -1143,6 +1148,8 @@ diquant.o: diquant.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dipixel.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimomod.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dibaslut.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimoopx.h \ ../../dcmimgle/include/dcmtk/dcmimgle/didispfn.h \ @@ -1156,8 +1163,6 @@ diquant.o: diquant.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpixel.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpobw.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ ../../dcmdata/include/dcmtk/dcmdata/dcuid.h diregist.o: diregist.cc ../../config/include/dcmtk/config/osconfig.h \ @@ -1249,12 +1254,13 @@ diregist.o: diregist.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimopx.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimomod.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dibaslut.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimoopx.h \ ../../dcmimgle/include/dcmtk/dcmimgle/didocu.h \ ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h dirgbimg.o: dirgbimg.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ @@ -1406,10 +1412,11 @@ diybrimg.o: diybrimg.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmimage/dicopx.h ../include/dcmtk/dcmimage/dilogger.h \ ../include/dcmtk/dcmimage/dicdefin.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dipixel.h \ - ../include/dcmtk/dcmimage/diybrpxt.h ../include/dcmtk/dcmimage/dicopxt.h \ + ../include/dcmtk/dcmimage/diybrpxt.h \ ../../ofstd/include/dcmtk/ofstd/ofbmanip.h \ ../../ofstd/include/dcmtk/ofstd/diag/stringop.def \ ../../ofstd/include/dcmtk/ofstd/diag/restrict.def \ + ../include/dcmtk/dcmimage/dicopxt.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dipxrept.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diinpx.h \ ../../dcmimgle/include/dcmtk/dcmimgle/didocu.h \ diff --git a/dcmimage/libsrc/dipitiff.cc b/dcmimage/libsrc/dipitiff.cc index c8904411..00f99460 100644 --- a/dcmimage/libsrc/dipitiff.cc +++ b/dcmimage/libsrc/dipitiff.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2022, OFFIS e.V. + * Copyright (C) 2001-2024, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -37,6 +37,9 @@ BEGIN_EXTERN_C #endif END_EXTERN_C +#if TIFFLIB_VERSION < 20050912 +#error TIFF library versions prior to 3.7.4 are not supported by DCMTK +#endif DiTIFFPlugin::DiTIFFPlugin() : DiPluginFormat() @@ -63,27 +66,27 @@ int DiTIFFPlugin::write( int stream_fd = fileno(stream); -#ifdef HAVE_WINDOWS_H - -#if TIFFLIB_VERSION < 20050912 -#error TIFF library versions prior to 3.7.4 are not supported by DCMTK on Win32 - critical API change! +#ifdef _WIN32 + + /* On Windows, TIFFFdOpen() expects a Windows HANDLE (which is a pointer + * type) instead of a file descriptor, but passes the file descriptor as an + * int. Despite HANDLE being a 64-bit type on Win64 and int being 32-bit, + * this is apparently safe, because Win64 guarantees to only use 32-bit + * handles, for interoperability reasons, as documented here: + * + * https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication + * + * Therefore, we use _get_osfhandle() to access the HANDLE underlying the + * file descriptor. + */ + +#ifdef __CYGWIN__ + stream_fd = OFstatic_cast(int, get_osfhandle(stream_fd)); +#else + stream_fd = OFstatic_cast(int, _get_osfhandle(stream_fd)); #endif -/* Older versions of libtiff expected a Win32 HANDLE when compiled on Windows - * instead of a file descriptor. The code below was needed to make that work. - * Libtiff version 3.7.4 and newer are known to use a file descriptor instead, - * but it is not completely clear at which libtiff release the API change happened. - * - * #ifdef __CYGWIN__ - * stream_fd = OFstatic_cast(int, get_osfhandle(stream_fd)); - * #else - * stream_fd =OFstatic_cast(int, _get_osfhandle(stream_fd)); - * #endif - */ - -#elif TIFFLIB_VERSION < 20041016 -#error TIFF library versions prior to 3.7.0 are not supported by DCMTK - TIFFCleanup is missing! -#endif +#endif /* _WIN32 */ /* create bitmap with 8 bits per sample */ void *data = OFconst_cast(void *, image->getOutputData(frame, 8 /*bits*/, 0 /*planar*/)); @@ -161,9 +164,10 @@ int DiTIFFPlugin::write( offset += bytesperrow; } TIFFFlushData(tif); + /* Clean up internal structures and free memory. * However, the file will be closed by the caller, therefore - * TIFFClose(tif) is not called. + * TIFFClose() is not called. */ TIFFCleanup(tif); } @@ -183,16 +187,19 @@ void DiTIFFPlugin::setCompressionType(DiTIFFCompression ctype) compressionType = ctype; } + void DiTIFFPlugin::setLZWPredictor(DiTIFFLZWPredictor pred) { predictor = pred; } + void DiTIFFPlugin::setRowsPerStrip(unsigned long rows) { rowsPerStrip = rows; } + OFString DiTIFFPlugin::getLibraryVersionString() { /* use first line only, omit copyright information */ diff --git a/dcmimage/libsrc/diqtctab.cc b/dcmimage/libsrc/diqtctab.cc index f49392d7..14cdb26f 100644 --- a/dcmimage/libsrc/diqtctab.cc +++ b/dcmimage/libsrc/diqtctab.cc @@ -98,27 +98,20 @@ OFCondition DcmQuantColorTable::computeHistogram( // compute initial maxval maxval = OFstatic_cast(DcmQuantComponent, -1); - DcmQuantColorHashTable *htable = NULL; // attempt to make a histogram of the colors, unclustered. // If at first we don't succeed, lower maxval to increase color // coherence and try again. This will eventually terminate. - OFBool done = OFFalse; - while (! done) + do { - htable = new DcmQuantColorHashTable(); - numColors = htable->addToHashTable(image, maxval, maxcolors); - if (numColors > 0) done = OFTrue; - else - { - delete htable; + DcmQuantColorHashTable htable; + if (htable.addToHashTable(image, maxval, maxcolors) > 0) + { + numColors = htable.createHistogram(array); + return EC_Normal; + } maxval = maxval/2; - } - } - - numColors = htable->createHistogram(array); - delete htable; - return EC_Normal; + } while (OFTrue); } diff --git a/dcmimgle/docs/dcmdspfn.man b/dcmimgle/docs/dcmdspfn.man index 69740a74..5bd573f3 100644 --- a/dcmimgle/docs/dcmdspfn.man +++ b/dcmimgle/docs/dcmdspfn.man @@ -196,6 +196,6 @@ longish and confusing command lines (an example is provided in file \section dcmdspfn_copyright COPYRIGHT -Copyright (C) 1999-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1999-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmimgle/docs/dcod2lum.man b/dcmimgle/docs/dcod2lum.man index fd07ca0c..3ead26a5 100644 --- a/dcmimgle/docs/dcod2lum.man +++ b/dcmimgle/docs/dcod2lum.man @@ -56,6 +56,6 @@ sample characteristics file monitors, cameras, printers and scanners. \section dcod2lum_copyright COPYRIGHT -Copyright (C) 2002-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2002-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmimgle/docs/dconvlum.man b/dcmimgle/docs/dconvlum.man index 29735441..5a195862 100644 --- a/dcmimgle/docs/dconvlum.man +++ b/dcmimgle/docs/dconvlum.man @@ -54,6 +54,6 @@ Barten's model (including GSDF). \section dconvlum_copyright COPYRIGHT -Copyright (C) 1999-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1999-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmimgle/include/dcmtk/dcmimgle/diinpxt.h b/dcmimgle/include/dcmtk/dcmimgle/diinpxt.h index 6a37c2d7..76c5de97 100644 --- a/dcmimgle/include/dcmtk/dcmimgle/diinpxt.h +++ b/dcmimgle/include/dcmtk/dcmimgle/diinpxt.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2021, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -166,12 +166,7 @@ class DiInputPixelTemplate */ virtual ~DiInputPixelTemplate() { -#if defined(HAVE_STD__NOTHROW) && defined(HAVE_NOTHROW_DELETE) - /* use a non-throwing delete (if available) */ operator delete[] (Data, std::nothrow); -#else - delete[] Data; -#endif } #include DCMTK_DIAGNOSTIC_PUSH @@ -189,6 +184,7 @@ class DiInputPixelTemplate T2 *p = Data; unsigned long i; const double absrange = getAbsMaxRange(); + const double absmin = getAbsMinimum(); const unsigned long ocnt = (absrange <= 10000000.0) ? OFstatic_cast(unsigned long, absrange) : 0 /* no LUT */; Uint8 *lut = NULL; if ((sizeof(T2) <= 2) && (ocnt > 0) && (Count > 3 * ocnt)) // optimization criteria @@ -198,7 +194,7 @@ class DiInputPixelTemplate { DCMIMGLE_DEBUG("using optimized routine with additional LUT"); OFBitmanipTemplate::zeroMem(lut, ocnt); - Uint8 *q = lut - OFstatic_cast(T2, getAbsMinimum()); + Uint8 *q = lut - OFstatic_cast(T2, absmin); for (i = Count; i != 0; --i) // fill lookup table *(q + *(p++)) = 1; q = lut; @@ -206,7 +202,7 @@ class DiInputPixelTemplate { if (*(q++) != 0) { - MinValue[0] = OFstatic_cast(T2, OFstatic_cast(double, i) + getAbsMinimum()); + MinValue[0] = OFstatic_cast(T2, OFstatic_cast(double, i) + absmin); break; } } @@ -215,7 +211,7 @@ class DiInputPixelTemplate { if (*(--q) != 0) { - MaxValue[0] = OFstatic_cast(T2, OFstatic_cast(double, i - 1) + getAbsMinimum()); + MaxValue[0] = OFstatic_cast(T2, OFstatic_cast(double, i - 1) + absmin); break; } } @@ -226,24 +222,24 @@ class DiInputPixelTemplate } else { // calculate min/max for selected range OFBitmanipTemplate::zeroMem(lut, ocnt); p = Data + PixelStart; - q = lut - OFstatic_cast(T2, getAbsMinimum()); - for (i = PixelCount; i != 0; --i) // fill lookup table + q = lut - OFstatic_cast(T2, absmin); + for (i = PixelCount; i != 0; --i) // fill lookup table *(q + *(p++)) = 1; q = lut; - for (i = 0; i < ocnt; ++i) // search for minimum + for (i = 0; i < ocnt; ++i) // search for minimum { if (*(q++) != 0) { - MinValue[1] = OFstatic_cast(T2, OFstatic_cast(double, i) + getAbsMinimum()); + MinValue[1] = OFstatic_cast(T2, OFstatic_cast(double, i) + absmin); break; } } q = lut + ocnt; - for (i = ocnt; i != 0; --i) // search for maximum + for (i = ocnt; i != 0; --i) // search for maximum { if (*(--q) != 0) { - MaxValue[1] = OFstatic_cast(T2, OFstatic_cast(double, i - 1) + getAbsMinimum()); + MaxValue[1] = OFstatic_cast(T2, OFstatic_cast(double, i - 1) + absmin); break; } } @@ -380,33 +376,22 @@ class DiInputPixelTemplate /* Bits Allocated is always a multiple of 8 (see above), same for bits of T1 */ const Uint32 byteFactor = bitsAllocated / 8; const Uint32 bytes_T1 = bitsof_T1 / 8; - const Uint32 count_T1 = (byteFactor == bytes_T1) ? PixelCount : (PixelCount * byteFactor + bytes_T1 - 1) / bytes_T1; + const Uint32 count_T1 = OFstatic_cast(Uint32, (byteFactor == bytes_T1) ? PixelCount : (PixelCount * byteFactor + bytes_T1 - 1) / bytes_T1); #ifdef DEBUG DCMIMGLE_TRACE("PixelCount: " << PixelCount << ", byteFactor: " << byteFactor << ", bytes_T1: " << bytes_T1 << ", count_T1: " << count_T1); #endif /* allocate temporary buffer, even number of bytes required for getUncompressedFrame() */ const Uint32 extraByte = ((sizeof(T1) == 1) && (count_T1 & 1)) ? 1 : 0; -#ifdef HAVE_STD__NOTHROW + /* use a non-throwing new here (if available) because the allocated buffer can be huge */ pixel = new (std::nothrow) T1[count_T1 + extraByte]; -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - pixel = new T1[count_T1 + extraByte]; - } - catch (STD_NAMESPACE bad_alloc const &) - { - pixel = NULL; - } -#endif if (pixel != NULL) { if (uncompressed) { DCMIMGLE_DEBUG("using partial read access to uncompressed pixel data"); - const Uint32 offset = PixelStart * byteFactor; - const Uint32 bufSize = PixelCount * byteFactor; + const Uint32 offset = OFstatic_cast(Uint32, PixelStart * byteFactor); + const Uint32 bufSize = OFstatic_cast(Uint32, PixelCount * byteFactor); const OFCondition status = pixelData->getPartialValue(pixel, offset, bufSize, fileCache); if (status.good()) { @@ -420,13 +405,13 @@ class DiInputPixelTemplate DCMIMGLE_DEBUG("using partial read access to compressed pixel data"); OFCondition status = EC_IllegalCall; OFString decompressedColorModel; - const Uint32 fsize = FrameSize * byteFactor; + const Uint32 fsize = OFstatic_cast(Uint32, FrameSize * byteFactor); for (Uint32 frame = 0; frame < NumberOfFrames; ++frame) { /* make sure that the buffer always has an even number of bytes as required for getUncompressedFrame() */ const Uint32 bufSize = (fsize & 1) ? fsize + 1 : fsize; - status = pixelData->getUncompressedFrame(document->getDataset(), FirstFrame + frame, fragment, - OFreinterpret_cast(Uint8 *, pixel) + lengthBytes, bufSize, decompressedColorModel, fileCache); + status = pixelData->getUncompressedFrame(document->getDataset(), OFstatic_cast(Uint32, FirstFrame + frame), + fragment, OFreinterpret_cast(Uint8 *, pixel) + lengthBytes, bufSize, decompressedColorModel, fileCache); if (status.good()) { DCMIMGLE_TRACE("successfully decompressed frame " << FirstFrame + frame); @@ -455,27 +440,16 @@ class DiInputPixelTemplate } if ((pixel != NULL) && (lengthBytes > 0)) { - const Uint32 length_T1 = lengthBytes / sizeof(T1); + const Uint32 length_T1 = OFstatic_cast(Uint32, lengthBytes / sizeof(T1)); /* need to split 'length' in order to avoid integer overflow for large pixel data */ const Uint32 length_B1 = lengthBytes / bitsAllocated; const Uint32 length_B2 = lengthBytes % bitsAllocated; // # old code: Count = ((lengthBytes * 8) + bitsAllocated - 1) / bitsAllocated; Count = 8 * length_B1 + (8 * length_B2 + bitsAllocated - 1) / bitsAllocated; unsigned long i; -#ifdef HAVE_STD__NOTHROW + /* use a non-throwing new here (if available) because the allocated buffer can be huge */ Data = new (std::nothrow) T2[Count]; -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - Data = new T2[Count]; - } - catch (STD_NAMESPACE bad_alloc const &) - { - Data = NULL; - } -#endif if (Data != NULL) { DCMIMGLE_TRACE("Input length: " << lengthBytes << " bytes, Pixel count: " << Count @@ -500,7 +474,7 @@ class DiInputPixelTemplate T2 smask = 0; for (i = bitsStored; i < bitsof_T2; ++i) smask |= OFstatic_cast(T2, 1 << i); - const Uint16 shift = highBit + 1 - bitsStored; + const Uint16 shift = OFstatic_cast(Uint16, highBit + 1 - bitsStored); if (shift == 0) { DCMIMGLE_DEBUG("convert input pixel data: case 1b (mask & sign)"); @@ -561,7 +535,7 @@ class DiInputPixelTemplate T2 smask = 0; for (i = bitsStored; i < bitsof_T2; ++i) smask |= OFstatic_cast(T2, 1 << i); - const Uint16 shift = highBit + 1 - bitsStored; + const Uint16 shift = OFstatic_cast(Uint16, highBit + 1 - bitsStored); for (i = length_T1; i != 0; --i) { value = *(p++) >> shift; @@ -571,6 +545,22 @@ class DiInputPixelTemplate value >>= bitsAllocated; } } + /* check whether there are any remaining pixel values to be processed */ + const signed int rest = OFstatic_cast(int, Count - length_T1 * times); + if (rest > 0) + { + /* usually, there is only a single final pixel value */ + if (rest == 1) + DCMIMGLE_TRACE(" there is still " << rest << " pixel value to be processed, so let's do it"); + else + DCMIMGLE_TRACE(" there are still " << rest << " pixel values to be processed, so let's do it"); + value = *(p++) >> shift; + for (j = OFstatic_cast(Uint16, rest); j != 0; --j) + { + *(q++) = expandSign(OFstatic_cast(T2, value & mask), sign, smask); + value >>= bitsAllocated; + } + } } } else if ((bitsof_T1 < bitsAllocated) && (bitsAllocated % bitsof_T1 == 0) // case 3: multiplicand of 8/16 @@ -603,7 +593,7 @@ class DiInputPixelTemplate T1 mask[bitsof_T1]; mask[0] = 1; for (i = 1; i < bitsof_T1; ++i) - mask[i] = (mask[i - 1] << 1) | 1; + mask[i] = OFstatic_cast(T1, (mask[i - 1] << 1) | 1); T2 smask = 0; for (i = bitsStored; i < bitsof_T2; ++i) smask |= OFstatic_cast(T2, 1 << i); @@ -643,6 +633,13 @@ class DiInputPixelTemplate skip -= times * bitsof_T1; } } + /* fill the remaining entry (if any) with the smallest value that is possible */ + if (q < Data + Count) + { + DCMIMGLE_TRACE("not enough data, filling last entry of input buffer with value = " << getAbsMinimum()); + *q = OFstatic_cast(T2, getAbsMinimum()); + } + } } else DCMIMGLE_DEBUG("cannot allocate memory buffer for 'Data' in DiInputPixelTemplate::convert()"); @@ -653,12 +650,7 @@ class DiInputPixelTemplate if (deletePixel) { /* delete temporary buffer */ -#if defined(HAVE_STD__NOTHROW) && defined(HAVE_NOTHROW_DELETE) - /* use a non-throwing delete (if available) */ operator delete[] (pixel, std::nothrow); -#else - delete[] pixel; -#endif } } diff --git a/dcmimgle/include/dcmtk/dcmimgle/diluptab.h b/dcmimgle/include/dcmtk/dcmimgle/diluptab.h index 4383f350..ce49e297 100644 --- a/dcmimgle/include/dcmtk/dcmimgle/diluptab.h +++ b/dcmimgle/include/dcmtk/dcmimgle/diluptab.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2011, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -26,6 +26,7 @@ #include "dcmtk/config/osconfig.h" #include "dcmtk/dcmdata/dctagkey.h" +#include "dcmtk/dcmdata/dcvrobow.h" #include "dcmtk/dcmimgle/dibaslut.h" #include "dcmtk/dcmimgle/diobjcou.h" @@ -107,6 +108,23 @@ class DCMTK_DCMIMGLE_EXPORT DiLookupTable const signed long first = -1, EI_Status *status = NULL); + + /** constructor + * + ** @param data element containing the LUT data (must be of VR=OW) + * @param descriptor element containing the LUT descriptor + * @param explanation element containing the LUT explanation (optional) + * @param descripMode mode specifying the use of the bits per table entry value + * @param first expected value for "first input value mapped" (optional) + * @param status pointer to image status variable (optional) + */ + DiLookupTable(const DcmOtherByteOtherWord &data, + const DcmUnsignedShort &descriptor, + const DcmLongString *explanation = NULL, + const EL_BitsPerTableEntry descripMode = ELM_UseValue, + const signed long first = -1, + EI_Status *status = NULL); + /** constructor * ** @param buffer pointer to array with LUT entries @@ -169,6 +187,19 @@ class DCMTK_DCMIMGLE_EXPORT DiLookupTable int compareLUT(const DcmUnsignedShort &data, const DcmUnsignedShort &descriptor); + /** compares current LUT with specified LUT + * + ** @param data element containing the LUT data (must be of VR=OW) + * @param descriptor element containing the LUT descriptor + * + ** @return true if LUTs are not equal (1 = invalid LUT / memory error, + * 2 = descriptor differs, + * 3 = data differs) + * false (0) otherwise + */ + int compareLUT(const DcmOtherByteOtherWord &data, + const DcmUnsignedShort &descriptor); + /** compares current LUT with specified LUT * @@ -207,6 +238,24 @@ class DCMTK_DCMIMGLE_EXPORT DiLookupTable const EL_BitsPerTableEntry descripMode = ELM_UseValue, EI_Status *status = NULL); + /** initialize lookup table + * + ** @param data array containing the LUT data + * @param count number of entries in LUT data array + * @param descriptor element containing the LUT descriptor + * @param explanation element containing the LUT explanation (optional) + * @param descripMode mode specifying the use of the bits per table entry value + * @param first expected value for "first input value mapped" (optional) + * @param status pointer to image status variable (optional) + */ + void Init(const Uint16 *data, + const unsigned long count, + const DcmUnsignedShort &descriptor, + const DcmLongString *explanation = NULL, + const EL_BitsPerTableEntry descripMode = ELM_UseValue, + const signed long first = -1, + EI_Status *status = NULL); + /** check (and possibly correct) lookup table for consistency * ** @param count number of LUT entries diff --git a/dcmimgle/include/dcmtk/dcmimgle/dimocpt.h b/dcmimgle/include/dcmtk/dcmimgle/dimocpt.h index 7172ea3a..4e3020e6 100644 --- a/dcmimgle/include/dcmtk/dcmimgle/dimocpt.h +++ b/dcmimgle/include/dcmtk/dcmimgle/dimocpt.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2010, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -82,7 +82,7 @@ class DiMonoCopyTemplate { if (pixel != NULL) { - this->Data = new T[this->getCount()]; + this->Data = new (std::nothrow) T[this->getCount()]; if (this->Data != NULL) OFBitmanipTemplate::copyMem(pixel, this->Data, this->getCount()); } diff --git a/dcmimgle/include/dcmtk/dcmimgle/dimoflt.h b/dcmimgle/include/dcmtk/dcmimgle/dimoflt.h index bf626c3b..505ffa55 100644 --- a/dcmimgle/include/dcmtk/dcmimgle/dimoflt.h +++ b/dcmimgle/include/dcmtk/dcmimgle/dimoflt.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2011, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -95,7 +95,7 @@ class DiMonoFlipTemplate { if (pixel != NULL) { - this->Data = new T[this->getCount()]; + this->Data = new (std::nothrow) T[this->getCount()]; if (this->Data != NULL) { if (horz && vert) diff --git a/dcmimgle/include/dcmtk/dcmimgle/dimoipxt.h b/dcmimgle/include/dcmtk/dcmimgle/dimoipxt.h index e746a68b..15f929ea 100644 --- a/dcmimgle/include/dcmtk/dcmimgle/dimoipxt.h +++ b/dcmimgle/include/dcmtk/dcmimgle/dimoipxt.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2021, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -28,6 +28,7 @@ #include "dcmtk/ofstd/ofbmanip.h" #include "dcmtk/ofstd/ofcast.h" #include "dcmtk/ofstd/ofdiag.h" /* for DCMTK_DIAGNOSTIC macros */ +#include "dcmtk/ofstd/oflimits.h" /* for OFnumeric_limits<> */ #include "dcmtk/dcmimgle/dimopxt.h" #include "dcmtk/dcmimgle/diinpx.h" @@ -55,16 +56,16 @@ class DiMonoInputPixelTemplate DiMonoModality *modality) : DiMonoPixelTemplate(pixel, modality) { - if ((pixel != NULL) && (this->Count > 0)) + if ((pixel != NULL) && (this->Modality != NULL) && (this->Count > 0)) { // check whether to apply any modality transform - if ((this->Modality != NULL) && this->Modality->hasLookupTable() && (bitsof(T1) <= MAX_TABLE_ENTRY_SIZE)) + if (this->Modality->hasLookupTable() && (bitsof(T1) <= MAX_TABLE_ENTRY_SIZE)) { modlut(pixel); // ignore modality LUT min/max values since the image does not necessarily have to use all LUT entries this->determineMinMax(); } - else if ((this->Modality != NULL) && this->Modality->hasRescaling()) + else if (this->Modality->hasRescaling()) { rescale(pixel, this->Modality->getRescaleSlope(), this->Modality->getRescaleIntercept()); this->determineMinMax(OFstatic_cast(T3, this->Modality->getMinValue()), OFstatic_cast(T3, this->Modality->getMaxValue())); @@ -72,9 +73,16 @@ class DiMonoInputPixelTemplate rescale(pixel); // "copy" or reference pixel data this->determineMinMax(OFstatic_cast(T3, this->Modality->getMinValue()), OFstatic_cast(T3, this->Modality->getMaxValue())); } - /* erase empty part of the buffer (= blacken the background) */ + /* erase empty part of the buffer */ if ((this->Data != NULL) && (this->InputCount < this->Count)) - OFBitmanipTemplate::zeroMem(this->Data + this->InputCount, this->Count - this->InputCount); + { + /* that means, fill the background with the smallest value that is possible */ + const T3 minOut = OFnumeric_limits::min(); + const T3 background = (this->Modality->getAbsMinimum() < OFstatic_cast(double, minOut)) ? minOut : OFstatic_cast(T3, this->Modality->getAbsMinimum()); + const size_t count = (this->Count - this->InputCount); + DCMIMGLE_DEBUG("filing empty part of the intermediate pixel data (" << count << " pixels) with value = " << OFstatic_cast(double, background)); + OFBitmanipTemplate::setMem(this->Data + this->InputCount, background, count); + } } } @@ -134,7 +142,7 @@ class DiMonoInputPixelTemplate this->Data = OFstatic_cast(T3 *, input->getDataPtr()); input->removeDataReference(); // avoid double deletion } else - this->Data = new T3[this->Count]; + this->Data = new (std::nothrow) T3[this->Count]; if (this->Data != NULL) { DCMIMGLE_DEBUG("applying modality transformation with LUT (" << mlut->getCount() << " entries)"); @@ -206,7 +214,7 @@ class DiMonoInputPixelTemplate this->Data = OFstatic_cast(T3 *, input->getDataPtr()); input->removeDataReference(); // avoid double deletion } else - this->Data = new T3[this->Count]; + this->Data = new (std::nothrow) T3[this->Count]; if (this->Data != NULL) { T3 *q = this->Data; diff --git a/dcmimgle/include/dcmtk/dcmimgle/dimopxt.h b/dcmimgle/include/dcmtk/dcmimgle/dimopxt.h index 532ee3a0..cf6fc8f4 100644 --- a/dcmimgle/include/dcmtk/dcmimgle/dimopxt.h +++ b/dcmimgle/include/dcmtk/dcmimgle/dimopxt.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2016, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -60,20 +60,9 @@ class DiMonoPixelTemplate MaxValue[0] = 0; MaxValue[1] = 0; // allocate buffer of given size -#ifdef HAVE_STD__NOTHROW + /* use a non-throwing new here (if available) because the allocated buffer can be huge */ Data = new (std::nothrow) T[Count]; -#else - /* make sure that the pointer is set to NULL in case of error */ - try - { - Data = new T[Count]; - } - catch (STD_NAMESPACE bad_alloc const &) - { - Data = NULL; - } -#endif if (Data == NULL) DCMIMGLE_DEBUG("cannot allocate memory buffer for 'Data' in DiMonoPixelTemplate constructor"); } @@ -114,12 +103,7 @@ class DiMonoPixelTemplate */ virtual ~DiMonoPixelTemplate() { -#if defined(HAVE_STD__NOTHROW) && defined(HAVE_NOTHROW_DELETE) - /* use a non-throwing delete (if available) */ operator delete[] (Data, std::nothrow); -#else - delete[] Data; -#endif } /** get integer representation diff --git a/dcmimgle/include/dcmtk/dcmimgle/dimorot.h b/dcmimgle/include/dcmtk/dcmimgle/dimorot.h index c4969569..1229adc5 100644 --- a/dcmimgle/include/dcmtk/dcmimgle/dimorot.h +++ b/dcmimgle/include/dcmtk/dcmimgle/dimorot.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2011, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -94,7 +94,7 @@ class DiMonoRotateTemplate { if (pixel != NULL) { - this->Data = new T[DiMonoPixelTemplate::getCount()]; + this->Data = new (std::nothrow) T[DiMonoPixelTemplate::getCount()]; if (this->Data != NULL) { if (degree == 90) diff --git a/dcmimgle/include/dcmtk/dcmimgle/dimosct.h b/dcmimgle/include/dcmtk/dcmimgle/dimosct.h index a437b818..c49fe7a5 100644 --- a/dcmimgle/include/dcmtk/dcmimgle/dimosct.h +++ b/dcmimgle/include/dcmtk/dcmimgle/dimosct.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2011, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -112,7 +112,7 @@ class DiMonoScaleTemplate { if (pixel != NULL) { - this->Data = new T[this->getCount()]; + this->Data = new (std::nothrow) T[this->getCount()]; if (this->Data != NULL) { const T value = OFstatic_cast(T, OFstatic_cast(double, DicomImageClass::maxval(bits)) * diff --git a/dcmimgle/include/dcmtk/dcmimgle/diutils.h b/dcmimgle/include/dcmtk/dcmimgle/diutils.h index 47f4ce87..74ecc7fa 100644 --- a/dcmimgle/include/dcmtk/dcmimgle/diutils.h +++ b/dcmimgle/include/dcmtk/dcmimgle/diutils.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2018, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -55,8 +55,7 @@ extern DCMTK_DCMIMGLE_EXPORT OFLogger DCM_dcmimgleLogger; /** @name configuration flags */ - -//@{ +///@{ /// compatibility with old ACR-NEMA images const unsigned long CIF_AcrNemaCompatibility = 0x0000001; @@ -98,7 +97,8 @@ const unsigned long CIF_DecompressCompletePixelData = 0x0000800; /// never access embedded overlays since this requires to load and uncompress the complete pixel data const unsigned long CIF_NeverAccessEmbeddedOverlays = 0x0001000; -//@} + +///@} // / true color color mode (for monochrome images only) diff --git a/dcmimgle/libsrc/Makefile.dep b/dcmimgle/libsrc/Makefile.dep index 8a4c4d1d..01b83d1c 100644 --- a/dcmimgle/libsrc/Makefile.dep +++ b/dcmimgle/libsrc/Makefile.dep @@ -66,6 +66,8 @@ dcmimage.o: dcmimage.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmimgle/diovpln.h ../include/dcmtk/dcmimgle/diutils.h \ ../include/dcmtk/dcmimgle/dimopx.h ../include/dcmtk/dcmimgle/dipixel.h \ ../include/dcmtk/dcmimgle/dimomod.h ../include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../include/dcmtk/dcmimgle/dibaslut.h ../include/dcmtk/dcmimgle/dimoopx.h \ ../include/dcmtk/dcmimgle/didispfn.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ @@ -77,7 +79,6 @@ dcmimage.o: dcmimage.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmimgle/dimo1img.h ../include/dcmtk/dcmimgle/didocu.h \ ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ ../include/dcmtk/dcmimgle/diregbas.h \ ../include/dcmtk/dcmimgle/diplugin.h @@ -661,13 +662,14 @@ diluptab.o: diluptab.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ + ../include/dcmtk/dcmimgle/dibaslut.h ../include/dcmtk/dcmimgle/diutils.h \ + ../include/dcmtk/dcmimgle/didefine.h \ ../../ofstd/include/dcmtk/ofstd/ofbmanip.h \ ../../ofstd/include/dcmtk/ofstd/diag/stringop.def \ ../../ofstd/include/dcmtk/ofstd/diag/restrict.def \ ../include/dcmtk/dcmimgle/diluptab.h \ - ../include/dcmtk/dcmimgle/dibaslut.h ../include/dcmtk/dcmimgle/diutils.h \ - ../include/dcmtk/dcmimgle/didefine.h \ ../include/dcmtk/dcmimgle/diobjcou.h ../include/dcmtk/dcmimgle/didocu.h \ ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h @@ -739,6 +741,8 @@ dimo1img.o: dimo1img.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmimgle/diovpln.h ../include/dcmtk/dcmimgle/diutils.h \ ../include/dcmtk/dcmimgle/dimopx.h ../include/dcmtk/dcmimgle/dipixel.h \ ../include/dcmtk/dcmimgle/dimomod.h ../include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../include/dcmtk/dcmimgle/dibaslut.h ../include/dcmtk/dcmimgle/dimoopx.h \ ../include/dcmtk/dcmimgle/didispfn.h dimo2img.o: dimo2img.cc ../../config/include/dcmtk/config/osconfig.h \ @@ -809,6 +813,8 @@ dimo2img.o: dimo2img.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmimgle/diovpln.h ../include/dcmtk/dcmimgle/diutils.h \ ../include/dcmtk/dcmimgle/dimopx.h ../include/dcmtk/dcmimgle/dipixel.h \ ../include/dcmtk/dcmimgle/dimomod.h ../include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../include/dcmtk/dcmimgle/dibaslut.h ../include/dcmtk/dcmimgle/dimoopx.h \ ../include/dcmtk/dcmimgle/didispfn.h dimoimg.o: dimoimg.cc ../../config/include/dcmtk/config/osconfig.h \ @@ -879,6 +885,8 @@ dimoimg.o: dimoimg.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmimgle/diovpln.h ../include/dcmtk/dcmimgle/diutils.h \ ../include/dcmtk/dcmimgle/dimopx.h ../include/dcmtk/dcmimgle/dipixel.h \ ../include/dcmtk/dcmimgle/dimomod.h ../include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../include/dcmtk/dcmimgle/dibaslut.h ../include/dcmtk/dcmimgle/dimoopx.h \ ../include/dcmtk/dcmimgle/didispfn.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ @@ -899,7 +907,6 @@ dimoimg.o: dimoimg.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmimgle/didocu.h \ ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ ../include/dcmtk/dcmimgle/diregbas.h dimoimg3.o: dimoimg3.cc ../../config/include/dcmtk/config/osconfig.h \ @@ -970,6 +977,8 @@ dimoimg3.o: dimoimg3.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmimgle/diovpln.h ../include/dcmtk/dcmimgle/diutils.h \ ../include/dcmtk/dcmimgle/dimopx.h ../include/dcmtk/dcmimgle/dipixel.h \ ../include/dcmtk/dcmimgle/dimomod.h ../include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../include/dcmtk/dcmimgle/dibaslut.h ../include/dcmtk/dcmimgle/dimoopx.h \ ../include/dcmtk/dcmimgle/didispfn.h \ ../include/dcmtk/dcmimgle/dimoipxt.h \ @@ -1049,6 +1058,8 @@ dimoimg4.o: dimoimg4.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmimgle/diovpln.h ../include/dcmtk/dcmimgle/diutils.h \ ../include/dcmtk/dcmimgle/dimopx.h ../include/dcmtk/dcmimgle/dipixel.h \ ../include/dcmtk/dcmimgle/dimomod.h ../include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../include/dcmtk/dcmimgle/dibaslut.h ../include/dcmtk/dcmimgle/dimoopx.h \ ../include/dcmtk/dcmimgle/didispfn.h \ ../include/dcmtk/dcmimgle/dimoipxt.h \ @@ -1128,6 +1139,8 @@ dimoimg5.o: dimoimg5.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmimgle/diovpln.h ../include/dcmtk/dcmimgle/diutils.h \ ../include/dcmtk/dcmimgle/dimopx.h ../include/dcmtk/dcmimgle/dipixel.h \ ../include/dcmtk/dcmimgle/dimomod.h ../include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../include/dcmtk/dcmimgle/dibaslut.h ../include/dcmtk/dcmimgle/dimoopx.h \ ../include/dcmtk/dcmimgle/didispfn.h \ ../include/dcmtk/dcmimgle/dimoipxt.h \ @@ -1156,7 +1169,16 @@ dimomod.o: dimomod.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ ../../dcmdata/include/dcmtk/dcmdata/dcuid.h \ ../include/dcmtk/dcmimgle/dimomod.h ../include/dcmtk/dcmimgle/diluptab.h \ - ../include/dcmtk/dcmimgle/dibaslut.h ../include/dcmtk/dcmimgle/diutils.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ ../../oflog/include/dcmtk/oflog/oflog.h \ ../../oflog/include/dcmtk/oflog/logger.h \ ../../oflog/include/dcmtk/oflog/config.h \ @@ -1182,29 +1204,20 @@ dimomod.o: dimomod.cc ../../config/include/dcmtk/config/osconfig.h \ ../../oflog/include/dcmtk/oflog/logmacro.h \ ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ + ../include/dcmtk/dcmimgle/dibaslut.h ../include/dcmtk/dcmimgle/diutils.h \ ../include/dcmtk/dcmimgle/didefine.h \ - ../include/dcmtk/dcmimgle/diobjcou.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../include/dcmtk/dcmimgle/didocu.h \ - ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ + ../include/dcmtk/dcmimgle/diobjcou.h ../include/dcmtk/dcmimgle/didocu.h \ ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ ../../ofstd/include/dcmtk/ofstd/offile.h \ ../../ofstd/include/dcmtk/ofstd/ofstd.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ - ../../ofstd/include/dcmtk/ofstd/ofcond.h \ - ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ - ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ - ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ - ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ @@ -1253,9 +1266,22 @@ dimoopx.o: dimoopx.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ ../include/dcmtk/dcmimgle/dibaslut.h \ - ../include/dcmtk/dcmimgle/diobjcou.h \ - ../../ofstd/include/dcmtk/ofstd/ofthread.h + ../include/dcmtk/dcmimgle/diobjcou.h dimopx.o: dimopx.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmimgle/dimopx.h \ ../../ofstd/include/dcmtk/ofstd/ofcast.h \ @@ -1299,10 +1325,23 @@ dimopx.o: dimopx.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ - ../include/dcmtk/dcmimgle/dibaslut.h \ - ../include/dcmtk/dcmimgle/diobjcou.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../include/dcmtk/dcmimgle/diinpx.h ../include/dcmtk/dcmimgle/dimoopx.h + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ + ../include/dcmtk/dcmimgle/dibaslut.h \ + ../include/dcmtk/dcmimgle/diobjcou.h ../include/dcmtk/dcmimgle/diinpx.h \ + ../include/dcmtk/dcmimgle/dimoopx.h diovdat.o: diovdat.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ ../../oflog/include/dcmtk/oflog/oflog.h \ @@ -1491,6 +1530,8 @@ diovlimg.o: diovlimg.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmimgle/diovpln.h ../include/dcmtk/dcmimgle/diutils.h \ ../include/dcmtk/dcmimgle/dimopx.h ../include/dcmtk/dcmimgle/dipixel.h \ ../include/dcmtk/dcmimgle/dimomod.h ../include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../include/dcmtk/dcmimgle/dibaslut.h ../include/dcmtk/dcmimgle/dimoopx.h \ ../include/dcmtk/dcmimgle/didispfn.h ../include/dcmtk/dcmimgle/dimopxt.h \ ../../ofstd/include/dcmtk/ofstd/ofbmanip.h \ @@ -1499,7 +1540,6 @@ diovlimg.o: diovlimg.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmimgle/dipxrept.h ../include/dcmtk/dcmimgle/didocu.h \ ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h diovpln.o: diovpln.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ diff --git a/dcmimgle/libsrc/dcmimage.cc b/dcmimgle/libsrc/dcmimage.cc index bc395a27..ed107264 100644 --- a/dcmimgle/libsrc/dcmimage.cc +++ b/dcmimgle/libsrc/dcmimage.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2024, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -210,6 +210,7 @@ void DicomImage::Init() *(q++) = c; } *q = '\0'; // end of C string + DCMIMGLE_DEBUG("filtered version of 'PhotometricInterpretation' = " << OFSTRING_GUARD(cstr)); while ((pin->Name != NULL) && (strcmp(pin->Name, cstr) != 0)) ++pin; delete[] cstr; diff --git a/dcmimgle/libsrc/dicielut.cc b/dcmimgle/libsrc/dicielut.cc index 1c5e64cc..13830fbc 100644 --- a/dcmimgle/libsrc/dicielut.cc +++ b/dcmimgle/libsrc/dicielut.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1999-2021, OFFIS e.V. + * Copyright (C) 1999-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -167,6 +167,8 @@ int DiCIELABLUT::createLUT(const Uint16 *ddl_tab, { if (Count == ddl_cnt) // check whether CIELAB LUT fits exactly to DISPLAY file { + /* use the standard "C" locale for proper decimal point */ + const STD_NAMESPACE locale oldLocale = stream->imbue(STD_NAMESPACE locale("C")); for (i = 0; i < ddl_cnt; ++i) { (*stream) << ddl_tab[i]; // DDL @@ -183,6 +185,8 @@ int DiCIELABLUT::createLUT(const Uint16 *ddl_tab, } (*stream) << OFendl; } + /* reset locale */ + stream->imbue(oldLocale); } else { DCMIMGLE_WARN("can't write curve data, wrong DISPLAY file or CIELAB LUT"); } diff --git a/dcmimgle/libsrc/didocu.cc b/dcmimgle/libsrc/didocu.cc index a0db6ec9..584fac75 100644 --- a/dcmimgle/libsrc/didocu.cc +++ b/dcmimgle/libsrc/didocu.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2024, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -175,7 +175,12 @@ void DiDocument::convertPixelData() status = PixelData->getDecompressedColorModel(OFstatic_cast(DcmItem *, Object), PhotometricInterpretation); if (status.bad()) { - DCMIMGLE_ERROR("can't determine 'PhotometricInterpretation' of decompressed image"); + if (Flags & CIF_AcrNemaCompatibility) + { + DCMIMGLE_WARN("can't determine 'PhotometricInterpretation' of decompressed image " + << "... assuming MONOCHROME2 (ACR-NEMA compatibility)"); + } else + DCMIMGLE_ERROR("can't determine 'PhotometricInterpretation' of decompressed image"); DCMIMGLE_DEBUG("DcmPixelData::getDecompressedColorModel() returned: " << status.text()); } } else { diff --git a/dcmimgle/libsrc/digsdlut.cc b/dcmimgle/libsrc/digsdlut.cc index d1aae668..fa931657 100644 --- a/dcmimgle/libsrc/digsdlut.cc +++ b/dcmimgle/libsrc/digsdlut.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1999-2021, OFFIS e.V. + * Copyright (C) 1999-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -181,6 +181,8 @@ int DiGSDFLUT::createLUT(const Uint16 *ddl_tab, { if (Count == ddl_cnt) // check whether GSDF LUT fits exactly to DISPLAY file { + /* use the standard "C" locale for proper decimal point */ + const STD_NAMESPACE locale oldLocale = stream->imbue(STD_NAMESPACE locale("C")); for (i = 0; i < ddl_cnt; ++i) { (*stream) << ddl_tab[i]; // DDL @@ -197,6 +199,8 @@ int DiGSDFLUT::createLUT(const Uint16 *ddl_tab, } (*stream) << OFendl; } + /* reset locale */ + stream->imbue(oldLocale); } else { DCMIMGLE_WARN("can't write curve data, wrong DISPLAY file or GSDF LUT"); } diff --git a/dcmimgle/libsrc/diimage.cc b/dcmimgle/libsrc/diimage.cc index 480235e3..0f525875 100644 --- a/dcmimgle/libsrc/diimage.cc +++ b/dcmimgle/libsrc/diimage.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2024, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -549,12 +549,18 @@ void DiImage::convertPixelData() { const unsigned long fsize = OFstatic_cast(unsigned long, Rows) * OFstatic_cast(unsigned long, Columns) * OFstatic_cast(unsigned long, SamplesPerPixel); - if ((BitsAllocated < 1) || (BitsStored < 1) || (BitsAllocated < BitsStored) || - (BitsStored > OFstatic_cast(Uint16, HighBit + 1))) + if ((BitsAllocated < 1) || (BitsStored < 1)) { ImageStatus = EIS_InvalidValue; - DCMIMGLE_ERROR("invalid values for 'BitsAllocated' (" << BitsAllocated << "), " - << "'BitsStored' (" << BitsStored << ") and/or 'HighBit' (" << HighBit << ")"); + DCMIMGLE_ERROR("invalid value(s) for 'BitsAllocated' (" << BitsAllocated << "), " + << "and/or 'BitsStored' (" << BitsStored << ")"); + return; + } + else if ((BitsAllocated < BitsStored) || (BitsAllocated <= HighBit) || ((BitsStored - 1) > HighBit)) + { + ImageStatus = EIS_InvalidValue; + DCMIMGLE_ERROR("invalid combination of values for 'BitsAllocated' (" << BitsAllocated << "), " + << "'BitsStored' (" << BitsStored << ") and 'HighBit' (" << HighBit << ")"); return; } else if ((evr == EVR_OB) && (BitsStored <= 8)) @@ -883,7 +889,7 @@ int DiImage::writeBMP(FILE *stream, result = 1; } /* delete pixel data */ - delete OFstatic_cast(char *, data); // type cast necessary to avoid compiler warnings using gcc >2.95 + delete[] OFstatic_cast(char *, data); } return result; } diff --git a/dcmimgle/libsrc/diluptab.cc b/dcmimgle/libsrc/diluptab.cc index 8c704fe9..2ded9097 100644 --- a/dcmimgle/libsrc/diluptab.cc +++ b/dcmimgle/libsrc/diluptab.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2021, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -24,8 +24,11 @@ #include "dcmtk/dcmdata/dcdeftag.h" #include "dcmtk/dcmdata/dcsequen.h" #include "dcmtk/dcmdata/dcitem.h" +#include "dcmtk/dcmdata/dcvrobow.h" #include "dcmtk/dcmdata/dcvrus.h" +#include "dcmtk/dcmimgle/dibaslut.h" +#include "dcmtk/dcmimgle/diutils.h" #include "dcmtk/ofstd/ofbmanip.h" #include "dcmtk/ofstd/ofcast.h" @@ -90,34 +93,32 @@ DiLookupTable::DiLookupTable(const DcmUnsignedShort &data, OriginalBitsAllocated(16), OriginalData(NULL) { - Uint16 us = 0; - const DcmElement *descElem = OFreinterpret_cast(const DcmElement *, &descriptor); - if (DiDocument::getElemValue(descElem, us, 0, OFTrue /*allowSigned*/) >= 3) // number of LUT entries + const Uint16 *dataPtr = NULL; + const unsigned long count = DiDocument::getElemValue(OFreinterpret_cast(const DcmElement *, &data), dataPtr); + Init(dataPtr, count, descriptor, explanation, descripMode, first, status); +} + + +DiLookupTable::DiLookupTable(const DcmOtherByteOtherWord &data, + const DcmUnsignedShort &descriptor, + const DcmLongString *explanation, + const EL_BitsPerTableEntry descripMode, + const signed long first, + EI_Status *status) + : DiBaseLUT(), + OriginalBitsAllocated(16), + OriginalData(NULL) +{ + /* check whether we have OW (16 bit data) since OB (8 bit) is not permitted for LUT data */ + if (data.getVR() == EVR_OW) { - Count = (us == 0) ? MAX_TABLE_ENTRY_COUNT : us; // see DICOM supplement 5: "0" => 65536 - DiDocument::getElemValue(descElem, FirstEntry, 1, OFTrue /*allowSigned*/); // can be SS or US (will be typecasted later) - if ((first >= 0) && (FirstEntry != OFstatic_cast(Uint16, first))) - { - DCMIMGLE_WARN("invalid value for 'FirstInputValueMapped' in lookup table (" - << FirstEntry << ") ... assuming " << first); - FirstEntry = OFstatic_cast(Uint16, first); - } - DiDocument::getElemValue(descElem, us, 2, OFTrue /*allowSigned*/); // bits per entry (only informational) - unsigned long count = DiDocument::getElemValue(OFreinterpret_cast(const DcmElement *, &data), Data); - OriginalData = OFstatic_cast(void *, OFconst_cast(Uint16 *, Data)); // store pointer to original data - if (explanation != NULL) - DiDocument::getElemValue(OFreinterpret_cast(const DcmElement *, explanation), Explanation); // explanation (free form text) - checkTable(count, us, descripMode, status); - } else { - if (status != NULL) - { - *status = EIS_MissingAttribute; - DCMIMGLE_ERROR("incomplete or missing 'LookupTableDescriptor' " << descriptor.getTag()); - } else { - DCMIMGLE_WARN("incomplete or missing 'LookupTableDescriptor' " << descriptor.getTag() - << " ... ignoring LUT"); - } - } + const Uint16 *dataPtr = NULL; + const unsigned long count = DiDocument::getElemValue(OFreinterpret_cast(const DcmElement *, &data), dataPtr); + Init(dataPtr, count, descriptor, explanation, descripMode, first, status); + } else { + DCMIMGLE_ERROR("invalid VR for 'LookupTableData' " << data.getTag()); + *status = EIS_InvalidImage; + } } @@ -158,7 +159,7 @@ void DiLookupTable::Init(const DiDocument *docu, Count = (us == 0) ? MAX_TABLE_ENTRY_COUNT : us; // see DICOM supplement 5: "0" => 65536 docu->getValue(descriptor, FirstEntry, 1, item, OFTrue /*allowSigned*/); // can be SS or US (will be typecasted later) docu->getValue(descriptor, us, 2, item, OFTrue /*allowSigned*/); // bits per entry (only informational) - unsigned long count = docu->getValue(data, Data, item); + const unsigned long count = docu->getValue(data, Data, item); OriginalData = OFstatic_cast(void *, OFconst_cast(Uint16 *, Data)); // store pointer to original data if (explanation != DCM_UndefinedTagKey) docu->getValue(explanation, Explanation, 0 /*vm pos*/, item); // explanation (free form text) @@ -176,6 +177,45 @@ void DiLookupTable::Init(const DiDocument *docu, } +void DiLookupTable::Init(const Uint16 *data, + const unsigned long count, + const DcmUnsignedShort &descriptor, + const DcmLongString *explanation, + const EL_BitsPerTableEntry descripMode, + const signed long first, + EI_Status *status) +{ + Uint16 us = 0; + const DcmElement *descElem = OFreinterpret_cast(const DcmElement *, &descriptor); + if (DiDocument::getElemValue(descElem, us, 0, OFTrue /*allowSigned*/) >= 3) // number of LUT entries + { + Count = (us == 0) ? MAX_TABLE_ENTRY_COUNT : us; // see DICOM supplement 5: "0" => 65536 + DiDocument::getElemValue(descElem, FirstEntry, 1, OFTrue /*allowSigned*/); // can be SS or US (will be typecasted later) + if ((first >= 0) && (FirstEntry != OFstatic_cast(Uint16, first))) + { + DCMIMGLE_WARN("invalid value for 'FirstInputValueMapped' in lookup table (" + << FirstEntry << ") ... assuming " << first); + FirstEntry = OFstatic_cast(Uint16, first); + } + DiDocument::getElemValue(descElem, us, 2, OFTrue /*allowSigned*/); // bits per entry (only informational) + Data = data; // store pointer to passed data + OriginalData = OFstatic_cast(void *, OFconst_cast(Uint16 *, Data)); // store pointer to original data + if (explanation != NULL) + DiDocument::getElemValue(OFreinterpret_cast(const DcmElement *, explanation), Explanation); // explanation (free form text) + checkTable(count, us, descripMode, status); + } else { + if (status != NULL) + { + *status = EIS_MissingAttribute; + DCMIMGLE_ERROR("incomplete or missing 'LookupTableDescriptor' " << descriptor.getTag()); + } else { + DCMIMGLE_WARN("incomplete or missing 'LookupTableDescriptor' " << descriptor.getTag() + << " ... ignoring LUT"); + } + } +} + + void DiLookupTable::checkTable(unsigned long count, Uint16 bits, const EL_BitsPerTableEntry descripMode, @@ -236,8 +276,11 @@ void DiLookupTable::checkTable(unsigned long count, for (i = Count; i != 0; --i) { value = *(p++); - if (((value >> 8) != 0) && (value & 0xff) != (value >> 8)) // lo-byte not equal to hi-byte and ... - cmp = 1; + if ((value >> 8) != 0) // if hi-byte is not zero + { + if ((value & 0xff) != (value >> 8)) // lo-byte not equal to hi-byte and ... + cmp = 1; + } if (value < MinValue) // get global minimum MinValue = value; if (value > MaxValue) // get global maximum @@ -521,6 +564,18 @@ DiLookupTable *DiLookupTable::createInverseLUT() const } +int DiLookupTable::compareLUT(const DcmOtherByteOtherWord &data, + const DcmUnsignedShort &descriptor) +{ + int result = 1; + DiBaseLUT *lut = new DiLookupTable(data, descriptor); + if (lut != NULL) + result = compare(lut); + delete lut; + return result; +} + + int DiLookupTable::compareLUT(const DcmUnsignedShort &data, const DcmUnsignedShort &descriptor) { diff --git a/dcmimgle/libsrc/dimoimg.cc b/dcmimgle/libsrc/dimoimg.cc index 5a6ba7c7..183722c8 100644 --- a/dcmimgle/libsrc/dimoimg.cc +++ b/dcmimgle/libsrc/dimoimg.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2024 OFFIS e.V. + * Copyright (C) 1996-2025 OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -598,7 +598,7 @@ DiMonoImage::~DiMonoImage() { delete InterData; delete OutputData; - delete OFstatic_cast(char *, OverlayData); // type cast necessary to avoid compiler warnings using gcc 2.95 + delete[] OFstatic_cast(char *, OverlayData); if (VoiLutData != NULL) VoiLutData->removeReference(); // only delete if object is no longer referenced if (PresLutData != NULL) @@ -950,7 +950,7 @@ const void *DiMonoImage::getOutputPlane(const int) const void DiMonoImage::deleteOverlayData() { - delete OFstatic_cast(char *, OverlayData); // type cast necessary to avoid compiler warnings using gcc 2.95 + delete[] OFstatic_cast(char *, OverlayData); OverlayData = NULL; } @@ -1717,18 +1717,19 @@ unsigned long DiMonoImage::createDIB(void *&data, data = new Uint32[count]; // allocated memory buffer if (data != NULL) { - Uint32 *q = OFstatic_cast(Uint32 *, data); - Uint32 value; + Uint8 *q = OFstatic_cast(Uint8 *, data); + Uint8 value; Uint16 x; Uint16 y; + int j; for (y = Rows; y != 0; --y) { for (x = Columns; x != 0; --x) { value = *(p++); // store gray value - *(q++) = (value << 16) | - (value << 8) | - value; // copy to the three RGB-planes + for (j = 3; j != 0; --j) + *(q++) = value; // copy to the three RGB-planes + *(q++) = 0; // alpha channel is always zero } p += nextRow; // jump (backwards) to next row } diff --git a/dcmiod/include/dcmtk/dcmiod/iccexample.h b/dcmiod/include/dcmtk/dcmiod/iccexample.h new file mode 100644 index 00000000..5875927a --- /dev/null +++ b/dcmiod/include/dcmtk/dcmiod/iccexample.h @@ -0,0 +1,96 @@ +/* + * + * Copyright (C) 2025, Open Connections GmbH + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation are maintained by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmiod + * + * Author: Michael Onken + * + * Purpose: Example ICC profile data for sRGB color space + * + */ + +#ifndef ICCEXAMPLE_H +#define ICCEXAMPLE_H + +// The given ICC profile is a small valid ICC profile for sRGB color space that can be used +// in case no other profile is available. It is based on the example profile given in +// https://github.com/saucecontrol/Compact-ICC-Profiles (sRGB-v2-magic.icc using 182-Point Curve) + +const unsigned char DCMTK_SRGB_ICC_SAMPLE[] = { + 0x00, 0x00, 0x02, 0xe0, 0x6c, 0x63, 0x6d, 0x73, 0x02, 0x10, 0x00, 0x00, + 0x73, 0x63, 0x6e, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20, + 0x07, 0xe2, 0x00, 0x03, 0x00, 0x14, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x1d, + 0x61, 0x63, 0x73, 0x70, 0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00, + 0x73, 0x61, 0x77, 0x73, 0x63, 0x74, 0x72, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x68, 0x61, 0x6e, 0x64, + 0x93, 0xb2, 0x34, 0xa9, 0x0e, 0xb0, 0x22, 0x8a, 0x98, 0xfd, 0x9a, 0xaf, + 0xa3, 0x67, 0x89, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x5f, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x0c, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x14, + 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x54, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x01, 0x78, + 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x01, 0x78, + 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x01, 0x78, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x43, 0x30, 0x00, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf3, 0x54, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xc9, + 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xa0, + 0x00, 0x00, 0x38, 0xf2, 0x00, 0x00, 0x03, 0x8f, 0x58, 0x59, 0x5a, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x96, 0x00, 0x00, 0xb7, 0x89, + 0x00, 0x00, 0x18, 0xda, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x24, 0xa0, 0x00, 0x00, 0x0f, 0x85, 0x00, 0x00, 0xb6, 0xc4, + 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x38, 0x00, 0x54, 0x00, 0x70, 0x00, 0x8c, + 0x00, 0xa8, 0x00, 0xc4, 0x00, 0xe1, 0x01, 0x00, 0x01, 0x22, 0x01, 0x46, + 0x01, 0x6d, 0x01, 0x95, 0x01, 0xc1, 0x01, 0xf0, 0x02, 0x20, 0x02, 0x55, + 0x02, 0x8b, 0x02, 0xc4, 0x03, 0x01, 0x03, 0x3f, 0x03, 0x82, 0x03, 0xc6, + 0x04, 0x0e, 0x04, 0x59, 0x04, 0xa7, 0x04, 0xf9, 0x05, 0x4c, 0x05, 0xa4, + 0x05, 0xfe, 0x06, 0x5c, 0x06, 0xbe, 0x07, 0x21, 0x07, 0x8a, 0x07, 0xf4, + 0x08, 0x63, 0x08, 0xd5, 0x09, 0x49, 0x09, 0xc3, 0x0a, 0x3f, 0x0a, 0xbf, + 0x0b, 0x42, 0x0b, 0xc9, 0x0c, 0x54, 0x0c, 0xe1, 0x0d, 0x74, 0x0e, 0x09, + 0x0e, 0xa2, 0x0f, 0x40, 0x0f, 0xe0, 0x10, 0x85, 0x11, 0x2d, 0x11, 0xda, + 0x12, 0x8a, 0x13, 0x3e, 0x13, 0xf6, 0x14, 0xb2, 0x15, 0x71, 0x16, 0x36, + 0x16, 0xfd, 0x17, 0xca, 0x18, 0x99, 0x19, 0x6e, 0x1a, 0x46, 0x1b, 0x22, + 0x1c, 0x03, 0x1c, 0xe7, 0x1d, 0xd0, 0x1e, 0xbd, 0x1f, 0xae, 0x20, 0xa4, + 0x21, 0x9e, 0x22, 0x9c, 0x23, 0x9f, 0x24, 0xa5, 0x25, 0xb1, 0x26, 0xc0, + 0x27, 0xd5, 0x28, 0xed, 0x2a, 0x0a, 0x2b, 0x2b, 0x2c, 0x51, 0x2d, 0x7c, + 0x2e, 0xaa, 0x2f, 0xde, 0x31, 0x16, 0x32, 0x52, 0x33, 0x94, 0x34, 0xd9, + 0x36, 0x24, 0x37, 0x73, 0x38, 0xc6, 0x3a, 0x20, 0x3b, 0x7c, 0x3c, 0xdf, + 0x3e, 0x45, 0x3f, 0xb0, 0x41, 0x21, 0x42, 0x96, 0x44, 0x10, 0x45, 0x8f, + 0x47, 0x12, 0x48, 0x9b, 0x4a, 0x28, 0x4b, 0xbb, 0x4d, 0x51, 0x4e, 0xee, + 0x50, 0x8f, 0x52, 0x35, 0x53, 0xe0, 0x55, 0x90, 0x57, 0x45, 0x59, 0x00, + 0x5a, 0xbe, 0x5c, 0x84, 0x5e, 0x4c, 0x60, 0x1b, 0x61, 0xef, 0x63, 0xc7, + 0x65, 0xa6, 0x67, 0x89, 0x69, 0x71, 0x6b, 0x5f, 0x6d, 0x51, 0x6f, 0x4a, + 0x71, 0x46, 0x73, 0x4a, 0x75, 0x51, 0x77, 0x5e, 0x79, 0x71, 0x7b, 0x88, + 0x7d, 0xa6, 0x7f, 0xc8, 0x81, 0xf0, 0x84, 0x1e, 0x86, 0x50, 0x88, 0x89, + 0x8a, 0xc5, 0x8d, 0x09, 0x8f, 0x51, 0x91, 0x9f, 0x93, 0xf3, 0x96, 0x4b, + 0x98, 0xab, 0x9b, 0x0e, 0x9d, 0x78, 0x9f, 0xe7, 0xa2, 0x5b, 0xa4, 0xd6, + 0xa7, 0x56, 0xa9, 0xdb, 0xac, 0x67, 0xae, 0xf7, 0xb1, 0x8f, 0xb4, 0x2a, + 0xb6, 0xcc, 0xb9, 0x74, 0xbc, 0x21, 0xbe, 0xd5, 0xc1, 0x8d, 0xc4, 0x4c, + 0xc7, 0x10, 0xc9, 0xda, 0xcc, 0xab, 0xcf, 0x7f, 0xd2, 0x5c, 0xd5, 0x3d, + 0xd8, 0x24, 0xdb, 0x12, 0xde, 0x04, 0xe0, 0xfe, 0xe3, 0xfc, 0xe7, 0x01, + 0xea, 0x0c, 0xed, 0x1c, 0xf0, 0x34, 0xf3, 0x50, 0xf6, 0x73, 0xf9, 0x9b, + 0xfc, 0xca, 0xff, 0xff +}; + +unsigned int DCMTK_SRGB_ICC_SAMPLE_LEN = 736; + +#endif // ICCEXAMPLE_H diff --git a/dcmiod/include/dcmtk/dcmiod/iodimage.h b/dcmiod/include/dcmtk/dcmiod/iodimage.h index 744e61fd..464dd08c 100644 --- a/dcmiod/include/dcmtk/dcmiod/iodimage.h +++ b/dcmiod/include/dcmtk/dcmiod/iodimage.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2021, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -110,6 +110,21 @@ public: { } + /** Set whether attribute values should be checked on writing, i.e. if writing + * should fail if attribute values violate their VR, VM, character set or value length. + * A missing but required value is always considered an error, independent of this setting. + * If set to OFFalse, writing will always succeed, even if attribute value constraints + * are violated. A warning instead of an error will be printed to the logger. + * @param doCheck If OFTrue, attribute value errors are handled as errors on writing, if OFFalse + * any errors are ignored. + */ + virtual void setValueCheckOnWrite(const OFBool doCheck) + { + m_ImagePixel.setValueCheckOnWrite(doCheck); + m_GeneralImage.setValueCheckOnWrite(doCheck); + DcmIODCommon::setValueCheckOnWrite(doCheck); + } + virtual void setGeneralImageModuleEnabled(const OFBool enabled) { m_GeneralImageModuleEnabled = enabled; diff --git a/dcmiod/include/dcmtk/dcmiod/iodrules.h b/dcmiod/include/dcmtk/dcmiod/iodrules.h index 50ff2e68..63c53d52 100644 --- a/dcmiod/include/dcmtk/dcmiod/iodrules.h +++ b/dcmiod/include/dcmtk/dcmiod/iodrules.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2021, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -107,6 +107,17 @@ public: */ virtual ~IODRules(); + /** Copy constructor, performs deep copy + * @param other The other rule set to copy from + */ + IODRules(const IODRules& other); + + /** Assignment operator, performs deep copy + * @param other The other rule set to copy from + * @return Reference to this object + */ + IODRules& operator=(const IODRules& other); + private: /// Map that holds all rules, accessible by their tag key OFMap m_Rules; diff --git a/dcmiod/include/dcmtk/dcmiod/iodtypes.h b/dcmiod/include/dcmtk/dcmiod/iodtypes.h index e66e01ef..cad2a8f5 100644 --- a/dcmiod/include/dcmtk/dcmiod/iodtypes.h +++ b/dcmiod/include/dcmtk/dcmiod/iodtypes.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2019, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -26,6 +26,7 @@ #include "dcmtk/dcmiod/ioddef.h" #include "dcmtk/oflog/oflog.h" #include "dcmtk/ofstd/ofcond.h" +#include "dcmtk/dcmdata/dcerror.h" // ---------------------------------------------------------------------------- // Define the loggers for this module @@ -57,6 +58,7 @@ extern DCMTK_DCMIOD_EXPORT const OFConditionConst IOD_EC_InvalidLaterality; extern DCMTK_DCMIOD_EXPORT const OFConditionConst IOD_EC_InvalidElementValue; extern DCMTK_DCMIOD_EXPORT const OFConditionConst IOD_EC_InvalidReference; extern DCMTK_DCMIOD_EXPORT const OFConditionConst IOD_EC_ReferencesOmitted; +extern DCMTK_DCMIOD_EXPORT const OFConditionConst IOD_EC_InvalidColorPalette; /** Class that wraps some constant definitions in the context of IODs */ @@ -64,19 +66,226 @@ class DCMTK_DCMIOD_EXPORT DcmIODTypes { public: + /** Base class to store pixel data for frames. Right now 8 and 16 bit are supported. + * Per default, the class will release memory in the destructor. + */ + struct FrameBase + { + /// Destructor + FrameBase() {} + /** Returns pixel data size in bytes + * @return Size of pixel data in bytes + */ + virtual size_t getLengthInBytes() const = 0; + + /** Get pointer to pixel data + * @return Pointer to pixel data + */ + virtual void* getPixelData() const = 0; + + /** Get bytes used per pixel + * @return Bytes per pixel (right now 8 or 16) + */ + virtual Uint8 bytesPerPixel() const = 0; + + /** Get value at given index as 8 bit value + * @param byteVal The value at the given index + * @param index The index to get the value from + * @return EC_Normal if successful, EC_IllegalCall if index is out of bounds + */ + virtual OFCondition getUint8AtIndex(Uint8 &byteVal, const size_t index) const =0; + + /** Get value at given index as 16 bit value + * @param shortVal The value at the given index + * @param index The index to get the value from + * @return EC_Normal if successful, EC_IllegalCall if index is out of bounds + */ + virtual OFCondition getUint16AtIndex(Uint16 &shortVal, const size_t index) const =0; + + /** Set whether Frame class should release memory (default) or whether it will + * be released externally. + * @param release OFTrue if Frame should release memory, OFFalse otherwise + */ + virtual void setReleaseMemory(OFBool release) = 0; + + /** Print frame data to string (for debugging purposes) + * @return String representation of frame data + */ + virtual OFString print() const = 0; + + /** Deconstructor, frees frame data if not disabled via setReleaseMemory() method + */ + virtual ~FrameBase() {} + }; + /** Struct representing a single frame */ - struct Frame + template + class Frame : public FrameBase { + public: + + /** Default constructor + */ + Frame() : m_pixData(NULL), m_numPixels(0), m_releaseMemory(OFTrue) {} + + /** Constructor that creates pixel data of the given size (amount of pixels) + * @param numPixels Number of pixels to allocate + */ + Frame(const size_t numPixels) : m_pixData(NULL), m_numPixels(numPixels), m_releaseMemory(OFTrue) + { + m_pixData = new PixelType[numPixels]; + } + + /** Constructor that takes over pixel data for managing + * @param pixelData Pointer to pixel data that is afterwards managed by this class + * @param sizeInBytes Size of pixel data in bytes + */ + Frame(PixelType* pixelData, const size_t sizeInBytes) : m_pixData(pixelData), m_numPixels(0), m_releaseMemory(OFTrue) + { + m_numPixels = sizeInBytes / sizeof(PixelType); + } + + /** Copy constructor, copies pixel data + * @param rhs Frame to copy + */ + Frame(const Frame& rhs) + { + delete[] m_pixData; + m_pixData = new PixelType[rhs.m_numPixels]; + memcpy(m_pixData, rhs.m_pixData, rhs.m_numPixels); + m_numPixels = rhs.m_numPixels; + m_releaseMemory = rhs.m_releaseMemory; + }; + + /** Assignment constructor, copies pixel data + * @param rhs Frame to copy from + * @return Reference to this object + */ + Frame& operator=(const Frame& rhs) + { + if (this != &rhs) + { + delete[] m_pixData; + m_pixData = new PixelType[rhs.m_numPixels]; + memcpy(m_pixData, rhs.m_pixData, rhs.m_numPixels); + m_numPixels = rhs.m_numPixels; + m_releaseMemory = rhs.m_releaseMemory; + } + return *this; + } + + /** Set whether pixel data should be release by this class (default) + * or is managed externally. + * @param release OFTrue if memory should be released, OFFalse otherwise + */ + virtual void setReleaseMemory(OFBool release) + { + m_releaseMemory = release; + } + + /** Get size of pixel data in bytes + * @return Size of pixel data in bytes + */ + virtual size_t getLengthInBytes() const + { + return m_numPixels * bytesPerPixel(); // PixelType is always 1 or 2 bytes + } + + /** Returns pointer to pixel data (untyped) + * @return Pointer to pixel data + */ + virtual void* getPixelData() const + { + return m_pixData; + } + + /** Returns pointer to pixel data (typed) + * @return Pointer to pixel data + */ + virtual PixelType* getPixelDataTyped() const + { + return m_pixData; + } + + /** Returns number of pixels (not necessarily bytes) in the frame + * @return Number of pixels + */ + virtual Uint8 bytesPerPixel() const + { + return sizeof(PixelType); + } + + /** Get value at given index as 8 bit value + * @param byteVal The value at the given index + * @param index The index to get the value from + * @return EC_Normal if successful, EC_IllegalCall if index is out of bounds + */ + virtual OFCondition getUint8AtIndex(Uint8 &byteVal, const size_t index) const + { + if (index >= m_numPixels) { + return EC_IllegalCall; + } + // Since sizeof(PixelType) is known during compile time an + // we would require C++11 to mark the condition as constexpr + // we disable the related warning in general. +#include DCMTK_DIAGNOSTIC_PUSH +#include DCMTK_DIAGNOSTIC_IGNORE_VISUAL_STUDIO_CONSTANT_EXPRESSION_WARNING +// Add range check for 16-bit to 8-bit conversion + if (sizeof(PixelType) == 2 && m_pixData[index] > 255) { + DCMIOD_ERROR("Value in the frame is too large to be cast to 8 bits"); + return EC_IllegalCall; + } + byteVal = static_cast(m_pixData[index]); + return EC_Normal; + } +#include DCMTK_DIAGNOSTIC_POP + + /** Get value at given index as 16 bit value + * @param shortVal The value at the given index + * @param index The index to get the value from + * @return EC_Normal if successful, EC_IllegalCall if index is out of bounds + */ + virtual OFCondition getUint16AtIndex(Uint16 &shortVal, const size_t index) const + { + if (index >= m_numPixels) { + return EC_IllegalCall; + } + shortVal = static_cast(m_pixData[index]); + return EC_Normal; + } + + /** Print frame data to string (for debugging purposes) + * @return String representation of frame data + */ + virtual OFString print() const + { + OFStringStream ss; + ss << "Frame with " << m_numPixels << " bytes:\n"; + for (size_t i = 0; i < m_numPixels; i++) + { + ss << STD_NAMESPACE hex << OFstatic_cast(Uint16, m_pixData[i]) << " "; + } + ss << "\n"; + return ss.str().c_str(); + } + /// Array for the pixel data bytes - Uint8* pixData; - /// Number of pixel data bytes (i.e.\ Bits Allocated) - size_t length; - /// Destructor, frees memory + PixelType* m_pixData; + /// Number of pixels in the frame + size_t m_numPixels; + // Denote whether to release memory in destructor + OFBool m_releaseMemory; + + /** Destructor, frees memory if not disabled via setReleaseMemory() method + */ ~Frame() { - delete[] pixData; - pixData = NULL; + if (m_releaseMemory) + { + delete[] m_pixData; + m_pixData = NULL; + } } }; @@ -136,4 +345,5 @@ private: ~DcmIODTypes() {}; }; + #endif // IODTYPES_H diff --git a/dcmiod/include/dcmtk/dcmiod/iodutil.h b/dcmiod/include/dcmtk/dcmiod/iodutil.h index 499e0cac..01446bd7 100644 --- a/dcmiod/include/dcmtk/dcmiod/iodutil.h +++ b/dcmiod/include/dcmtk/dcmiod/iodutil.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -106,7 +106,7 @@ public: * (Would be 'const' if the methods from 'dcmdata' would also * be 'const') * @param delem DICOM element that is set to a copy of the dataset's - * orinal element + * original element * @param rule Rule describing parameters to be checked on element. * @return EC_Normal if element could be retrieved and value is correct, an * error code otherwise @@ -847,6 +847,24 @@ public: container.clear(); } + + /** Deletes all elements from given map and calls "delete" on each + * of them to clear memory. + * @param container The map that should be cleared. Must contain + * pointers to objects that are allocated on the heap. + */ + template + static void freeMap(Map& container) + { + typename Map::iterator it = container.begin(); + while (it != container.end()) + { + delete (*it).second; + it++; + } + container.clear(); + } + /** Clones and copies all elements from source to destination container by * copy constructing all elements. * If copying fails (because memory is exhausted), EC_MemoryExhausted is returned @@ -910,6 +928,12 @@ public: return 0; } + /** Set the current date and time on the given module by using setContentDate() + * and setContentTime() methods. The current date and time is retrieved + * from the system clock. + * @param module The module to set the date and time on + * @return EC_Normal if successful, an error code otherwise + */ template static OFCondition setContentDateAndTimeNow(ModuleType& module) { @@ -981,7 +1005,7 @@ public: static OFCondition extractBinaryFrames(Uint8* pixData, const size_t numFrames, const size_t bitsPerFrame, - OFVector& results); + OFVector& results); /** Resets the given condition to EC_Normal if checkValue is true and * prints a related message as a warning to the debug logger. @@ -993,6 +1017,23 @@ public: */ static void resetConditionIfCheckDisabled(OFCondition& result, const OFBool checkValue, DcmElement& elem); + + /** If checkValue is true, the given OFCondition for certain errors (listed below), prints + * a debug message if the condition is not good, and resets the condition + * to EC_Normal. If checkValue is false, the method does nothing. + * The following error codes are handled, i.e. reset: + * EC_ValueRepresentationViolated + * EC_MaximumLengthViolated + * EC_InvalidCharacter + * EC_ValueMultiplicityViolated + * @param result The condition to check. Only EC_ValueRepresentationViolated, EC_MaximumLengthViolated, + * EC_InvalidCharacter and EC_ValueMultiplicityViolated are handled. + * @param checkValue If this value is true, the listed condition are reset to EC_Normal; otherwise the + * condition is not changed and no message is printed. + * @param elem Used if the condition is reset to EC_Normal to print a message in case of a "reset" + */ + static void resetValueCheckResult(OFCondition& result, const OFBool checkValue, DcmElement& elem); + private: // We only have static functions so we do not need an instance of // this class so far. diff --git a/dcmiod/include/dcmtk/dcmiod/modbase.h b/dcmiod/include/dcmtk/dcmiod/modbase.h index 4346e3e7..6d02e0e8 100644 --- a/dcmiod/include/dcmtk/dcmiod/modbase.h +++ b/dcmiod/include/dcmtk/dcmiod/modbase.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -83,7 +83,7 @@ public: * An attribute is considered belonging to the module if there are rules * marked as belonging to this module via the rule's module name. */ - void clearData(); + virtual void clearData(); /** Set missing values by inventing "default values". Automatically * called during write() in IODComponent. In this bas class implementation, diff --git a/dcmiod/include/dcmtk/dcmiod/modequipment.h b/dcmiod/include/dcmtk/dcmiod/modequipment.h index c5edb189..149d6ba1 100644 --- a/dcmiod/include/dcmtk/dcmiod/modequipment.h +++ b/dcmiod/include/dcmtk/dcmiod/modequipment.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2019, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -168,6 +168,12 @@ public: */ virtual OFCondition getSoftwareVersions(OFString& value, const signed long pos = 0) const; + /** Get a copy altogether as EquipmentInfo + * @return EquipmentInfo object containing all relevant information + * If some data is not available, it will contain an empty string + */ + virtual IODGeneralEquipmentModule::EquipmentInfo getEquipmentInfo() const; + /** Set Manufacturer * @param value Value to be set (single value only) or "" for no value * @param checkValue Check 'value' for conformance with VR (LO) and VM (1) diff --git a/dcmiod/include/dcmtk/dcmiod/modgeneralimage.h b/dcmiod/include/dcmtk/dcmiod/modgeneralimage.h index 8d4a09a4..e533300d 100644 --- a/dcmiod/include/dcmtk/dcmiod/modgeneralimage.h +++ b/dcmiod/include/dcmtk/dcmiod/modgeneralimage.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2019, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -329,7 +329,7 @@ public: virtual OFCondition setIrradiationEventUID(const OFString& value, const OFBool checkValue = OFTrue); private: - /// The module's name ("GeneralIamgeModule") + /// The module's name ("GeneralImageModule") static const OFString m_ModuleName; }; diff --git a/dcmiod/include/dcmtk/dcmiod/modiccprofile.h b/dcmiod/include/dcmtk/dcmiod/modiccprofile.h new file mode 100644 index 00000000..e5052b1a --- /dev/null +++ b/dcmiod/include/dcmtk/dcmiod/modiccprofile.h @@ -0,0 +1,132 @@ +/* + * + * Copyright (C) 2024-2025, Open Connections GmbH + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation are maintained by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmiod + * + * Author: Michael Onken + * + * Purpose: Class for managing the ICC Profile Module + * + */ + +#ifndef MODICCPROFILE_H +#define MODICCPROFILE_H + +#include "dcmtk/config/osconfig.h" + +#include "dcmtk/dcmiod/iodrules.h" +#include "dcmtk/dcmiod/modbase.h" +#include "dcmtk/ofstd/ofcond.h" +#include "dcmtk/ofstd/oftypes.h" + +/** Class representing the ICC Profile Module: + * + * ICC Profile (0028,2000): (OB, 1, 1) + * Color Space (0028,2002): (CS, 3, 1) + */ +class DCMTK_DCMIOD_EXPORT IODICCProfileModule : public IODModule +{ + +public: + + /** Constructor + * @param item The item to be used for data storage. If NULL, the + * class creates an empty data container. + * @param rules The rule set for this class. If NULL, the class creates + * one from scratch and adds its values. + */ + IODICCProfileModule(OFshared_ptr item, OFshared_ptr rules); + + /** Constructor + */ + IODICCProfileModule(); + + /** Destructor + */ + virtual ~IODICCProfileModule(); + + /** Clear all attributes from the data that are handled by this module. + * An attribute is considered belonging to the module if there are rules + * marked as belonging to this module via the rule's module name. + */ + virtual void clearData(); + + /** Resets rules to their original values + */ + virtual void resetRules(); + + /** Get name of module + * @return Name of the module ("ICCProfileModule") + */ + virtual OFString getName() const; + + /** Read attributes from given item into this class + * @param source The source to read from + * @param clearOldData If OFTrue, old data is cleared before reading. Otherwise + * old data is overwritten (or amended) + * @result EC_Normal if reading was successful, error otherwise + */ + virtual OFCondition read(DcmItem& source, const OFBool clearOldData = OFTrue); + + /** Write attributes from this class into given item + * @param destination The item to write to + * @result EC_Normal if writing was successful, error otherwise + */ + virtual OFCondition write(DcmItem& destination); + + /** Get the ICC Color Profile data + * @param value Returns a reference to the icc profile data + * @param numBytes Number of bytes in the icc profile data returned in value parameter + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getICCProfile(const Uint8*& value, Uint32& numBytes); + + /** Get Color Space + * @param value Reference to variable in which a copy of the value should be stored + * @param pos Index of the value to get (0..vm-1), -1 for all components + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getColorSpace(OFString& value, const signed long pos = 0) const; + + /** Set ICC Profile data + * @param value The value of ICC Profile (will be copied) + * @param numBytes Number of bytes in the icc profile data (value parameter) + * @param checkValue Check value for conformance with VR (OB) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition + setICCProfile(const Uint8* value, const unsigned long numBytes, const OFBool checkValue = OFTrue); + + /** Set default ICC Profile data (SRGB) + * @param setColorSpaceDescription If OFTrue, Color Space Description (0028,2004) + * is set to "SRGB" when setting the default profile. If not, this attribute + * is not touched by this method (i.e. by default it remains unset). + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setDefaultProfile(const OFBool& setColorSpaceDescription = OFTrue); + + /** Set Color Space + * @param value The value of Color Space + * @param checkValue Check value for conformance with VR (CS) and VM (1) if enabled + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setColorSpace(const OFString& value, const OFBool checkValue = OFTrue); + +private: + + /// The module's name ("ICCProfileModule") + static const OFString m_ModuleName; + +}; + +#endif // MODICCPROFILE_H diff --git a/dcmiod/include/dcmtk/dcmiod/modimagepixelvariant.h b/dcmiod/include/dcmtk/dcmiod/modimagepixelvariant.h index f06a92ea..4f99e98c 100644 --- a/dcmiod/include/dcmtk/dcmiod/modimagepixelvariant.h +++ b/dcmiod/include/dcmtk/dcmiod/modimagepixelvariant.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2016-2019, Open Connections GmbH + * Copyright (C) 2016-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -257,6 +257,20 @@ public: return pBase->read(dataset); return IOD_EC_InvalidPixelData; } + + /** Set whether attribute values should be checked on writing, i.e. if writing + * should fail if attribute values violate their VR, VM, character set or value length. + * A missing but required value is always considered an error, independent of this setting. + * If set to OFFalse, writing will always succeed, even if attribute value constraints + * are violated. A warning instead of an error will be printed to the logger. + * @param doCheck If OFTrue, attribute value errors are handled as errors on writing, if OFFalse + * any errors are ignored. + */ + void setValueCheckOnWrite(const OFBool doCheck) + { + if (IODImagePixelBase* pBase = OFvisit(IODImagePixelVariantBaseVisitor(), *this)) + pBase->setValueCheckOnWrite(doCheck); + } }; #endif // MODIMAGEPIXELBASE_H diff --git a/dcmiod/include/dcmtk/dcmiod/modpalettecolorlut.h b/dcmiod/include/dcmtk/dcmiod/modpalettecolorlut.h new file mode 100644 index 00000000..1956c08f --- /dev/null +++ b/dcmiod/include/dcmtk/dcmiod/modpalettecolorlut.h @@ -0,0 +1,523 @@ +/* + * + * Copyright (C) 2024-2025, Open Connections GmbH + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation are maintained by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmiod + * + * Author: Michael Onken + * + * Purpose: Class for managing the Palette Color Lookup Table Module + * + */ + +#ifndef MODPALETTECOLORLUT_H +#define MODPALETTECOLORLUT_H + +#include "dcmtk/config/osconfig.h" + +#include "dcmtk/dcmdata/dctagkey.h" +#include "dcmtk/ofstd/ofmem.h" +#include "dcmtk/dcmiod/iodrules.h" +#include "dcmtk/dcmiod/modbase.h" +#include "dcmtk/ofstd/ofcond.h" +#include "dcmtk/ofstd/oftypes.h" + +/** Class representing the Palette Color Lookup Table Module: + * + * Red Palette Color Lookup Table​ Descriptor (0028,1101): (US or SS, 3, 1) + * Green Palette Color Lookup Table​ Descriptor (0028,1102): (US or SS, 3, 1) + * Blue Palette Color Lookup Table​ Descriptor (0028,1103): (US or SS, 3, 1) + * Palette Color Lookup Table UID (0028,1199): (UI, 1, 3) + * Red Palette Color Lookup Table​ Data​ (0028,1201): (OW, 1-n, 1C) + * Green Palette Color Lookup Table​ Data​ (0028,1202): (OW, 1-n, 1C) + * Blue Palette Color Lookup Table​ Data​ (0028,1203): (OW, 1-n, 1C) + * Segmented Red Palette Color Lookup Table​ Data​ (0028,1221): (OW, 1-n, 1C) + * Segmented Green Palette Color Lookup Table​ Data​ (0028,1222): (OW, 1-n, 1C) + * Segmented Blue Palette Color Lookup Table​ Data​ (0028,1223): (OW, 1-n, 1C) + */ +class DCMTK_DCMIOD_EXPORT IODPaletteColorLUTModule : public IODModule +{ + +public: + /** Constructor + * @param item The item to be used for data storage. If NULL, the + * class creates an empty data container. + * @param rules The rule set for this class. If NULL, the class creates + * one from scratch and adds its values. + */ + IODPaletteColorLUTModule(OFshared_ptr item, OFshared_ptr rules); + + /** Constructor + */ + IODPaletteColorLUTModule(); + + /** Destructor + */ + virtual ~IODPaletteColorLUTModule(); + + /** Clear all attributes from the data that are handled by this module. + * An attribute is considered belonging to the module if there are rules + * marked as belonging to this module via the rule's module name. + */ + virtual void clearData(); + + /** Resets rules to their original values + */ + virtual void resetRules(); + + /** Get name of module + * @return Name of the module ("PaletteColorLookupTableModule") + */ + virtual OFString getName() const; + + /** Read attributes from given item into this class + * @param source The source to read from + * @param clearOldData If OFTrue, old data is cleared before reading. Otherwise + * old data is overwritten (or amended) + * @result EC_Normal if reading was successful, error otherwise + */ + virtual OFCondition read(DcmItem& source, const OFBool clearOldData = OFTrue); + + /** Write attributes from this class into given item + * @param destination The item to write to + * @result EC_Normal if writing was successful, error otherwise + */ + virtual OFCondition write(DcmItem& destination); + + // ---------------- Getters ----------------------------- + + /** Returns the number of bits used per LUT data entry. + * If the number of bits is not the same for all LUTs, 0 is returned, indicating an error. + * @return The number of bits used per LUT data entry (8 or 16) + */ + virtual Uint8 numBits(); + + /** Get the Red Palette Color Lookup Table​ Descriptor + * @param value The value of Red Palette Color Lookup Table​ Descriptor + * @param pos The position of the value to be retrieved (0..2) + * @return EC_Normal if value is found, an error code otherwise + */ + virtual OFCondition getRedPaletteColorLookupTableDescriptor(Uint16& value, const unsigned long pos = 0) const; + + /** Get the Green Palette Color Lookup Table​ Descriptor + * @param value The value of Green Palette Color Lookup Table​ Descriptor + * @param pos The position of the value to be retrieved (0..2) + * @return EC_Normal if value is found, an error code otherwise + */ + virtual OFCondition getGreenPaletteColorLookupTableDescriptor(Uint16& value, const unsigned long pos = 0) const; + + /** Get the Blue Palette Color Lookup Table​ Descriptor + * @param value The value of Blue Palette Color Lookup Table​ Descriptor + * @param pos The position of the value to be retrieved (0..2) + * @return EC_Normal if value is found, an error code otherwise + */ + virtual OFCondition getBluePaletteColorLookupTableDescriptor(Uint16& value, const unsigned long pos = 0) const; + + /** Get the Palette Color Lookup Table UID + * @param value Reference to variable in which the value should be stored + * @param pos Index of the value to get (0..vm-1), -1 for all components + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getPaletteColorLookupTableUID(OFString& value, const signed long pos = 0) const; + + /** Get the Red Palette Color Lookup Table​ Data​ (16 bit version) + * @param dataCopy Reference to variable in which a copy of the value should be stored + * @param numEntries Number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getRedPaletteColorLookupTableData(const Uint16*& dataCopy, unsigned long& numEntries); + + /** Get the Red Palette Color Lookup Table​ Data​ (8 bit version) + * @param dataCopy Reference to variable in which a copy of the value should be stored + * @param numEntries Number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getRedPaletteColorLookupTableData(const Uint8*& dataCopy, unsigned long& numEntries); + + /** Get the Green Palette Color Lookup Table​ Data​ (16 bit version) + * @param dataCopy Reference to variable in which a copy of the value should be stored + * @param numEntries Number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual + OFCondition getGreenPaletteColorLookupTableData(const Uint16*& dataCopy, unsigned long& numEntries); + + /** Get the Green Palette Color Lookup Table​ Data​ (8 bit version) + * @param dataCopy Reference to variable in which a copy of the value should be stored + * @param numEntries Number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual + OFCondition getGreenPaletteColorLookupTableData(const Uint8*& dataCopy, unsigned long& numEntries); + + /** Get the Blue Palette Color Lookup Table​ Data​ (16 bit version) + * @param dataCopy Reference to variable in which a copy of the value should be stored + * @param numEntries Number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual + OFCondition getBluePaletteColorLookupTableData(const Uint16*& dataCopy, unsigned long& numEntries); + + + /** Get the Blue Palette Color Lookup Table​ Data​ (8 bit version) + * @param dataCopy Reference to variable in which a copy of the value should be stored + * @param numEntries Number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual + OFCondition getBluePaletteColorLookupTableData(const Uint8*& dataCopy, unsigned long& numEntries); + + + /** Get the Segmented Red Palette Color Lookup Table​ Data​ + * @param dataCopy Reference to variable in which a copy of the value should be stored + * @param numEntries Number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getSegmentedRedPaletteColorLookupTableData(const Uint16*& dataCopy, unsigned long& numEntries); + + /** Get the Segmented Red Palette Color Lookup Table​ Data​ (8 bit version) + * @param dataCopy Reference to variable in which a copy of the value should be stored + * @param numEntries Number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getSegmentedRedPaletteColorLookupTableData(const Uint8*& dataCopy, unsigned long& numEntries); + + /** Get the Segmented Green Palette Color Lookup Table​ Data​ (16 bit version) + * @param dataCopy Reference to variable in which a copy of the value should be stored + * @param numEntries Number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getSegmentedGreenPaletteColorLookupTableData(const Uint16*& dataCopy, unsigned long& numEntries); + + /** Get the Segmented Green Palette Color Lookup Table​ Data​ (8 bit version) + * @param dataCopy Reference to variable in which a copy of the value should be stored + * @param numEntries Number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getSegmentedGreenPaletteColorLookupTableData(const Uint8*& dataCopy, unsigned long& numEntries); + + /** Get the Segmented Blue Palette Color Lookup Table​ Data​ (16 bit version) + * @param dataCopy Reference to variable in which a copy of the value should be stored + * @param numEntries Number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getSegmentedBluePaletteColorLookupTableData(const Uint16*& dataCopy, unsigned long& numEntries); + + /** Get the Segmented Blue Palette Color Lookup Table​ Data​ (8 bit version) + * @param dataCopy Reference to variable in which a copy of the value should be stored + * @param numEntries Number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getSegmentedBluePaletteColorLookupTableData(const Uint8*& dataCopy, unsigned long& numEntries); + + // ---------------- Setters ----------------------------- + + /** Set the Red Palette Color Lookup Table​ Descriptor + * @param value The value of Red Palette Color Lookup Table​ Descriptor + * @param pos The position of the value to be set (0..2) + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setRedPaletteColorLookupTableDescriptor(const Uint16& value, const unsigned long pos = 0); + + /** Set the Green Palette Color Lookup Table​ Descriptor + * @param value The value of Green Palette Color Lookup Table​ Descriptor + * @param pos The position of the value to be set (0..2) + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setGreenPaletteColorLookupTableDescriptor(const Uint16& value, const unsigned long pos = 0); + + /** Set the Blue Palette Color Lookup Table​ Descriptor + * @param value The value of Blue Palette Color Lookup Table​ Descriptor + * @param pos The position of the value to be set (0..2) + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setBluePaletteColorLookupTableDescriptor(const Uint16& value, const unsigned long pos = 0); + + /** Set the Palette Color Lookup Table UID + * @param value The value of Palette Color Lookup Table UID + * @param checkValue Check value for conformance with VR (UI) and VM (1) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setPaletteColorLookupTableUID(const OFString& value, const OFBool checkValue = OFTrue); + + /** Set the Red Palette Color Lookup Table​ Data​ (16 bit version) + * @param value The value of Red Palette Color Lookup Table​ Data​ + * @param numEntries Number of entries in the lookup table + * @param checkValue Check value for conformance with VR (OW) and VM (1-n) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition + setRedPaletteColorLookupTableData(const Uint16* value, const unsigned long numEntries, const OFBool checkValue = OFTrue); + + /** Set the Red Palette Color Lookup Table​ Data​ (8 bit version) + * @param value The value of Red Palette Color Lookup Table​ Data​ + * @param numEntries Number of entries in the lookup table + * @param checkValue Check value for conformance with VR (OW) and VM (1-n) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition + setRedPaletteColorLookupTableData(const Uint8* value, const unsigned long numEntries, const OFBool checkValue = OFTrue); + + + /** Set the Green Palette Color Lookup Table​ Data​ (16 bit version) + * @param value The value of Red Palette Color Lookup Table​ Data​ + * @param numEntries Number of entries in the lookup table + * @param checkValue Check value for conformance with VR (OW) and VM (1-n) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + + virtual OFCondition + setGreenPaletteColorLookupTableData(const Uint16* value, const unsigned long numEntries, const OFBool checkValue = OFTrue); + + /** Set the Green Palette Color Lookup Table​ Data​ (8 bit version) + * @param value The value of Red Palette Color Lookup Table​ Data​ + * @param numEntries Number of entries in the lookup table + * @param checkValue Check value for conformance with VR (OW) and VM (1-n) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + + virtual OFCondition + setGreenPaletteColorLookupTableData(const Uint8* value, const unsigned long numEntries, const OFBool checkValue = OFTrue); + + /** Set the Blue Palette Color Lookup Table​ Data​ (16 bit version) + * @param value The value of Red Palette Color Lookup Table​ Data​ + * @param numEntries Number of entries in the lookup table + * @param checkValue Check value for conformance with VR (OW) and VM (1-n) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition + setBluePaletteColorLookupTableData(const Uint16* value, const unsigned long numEntries, const OFBool checkValue = OFTrue); + + /** Set the Blue Palette Color Lookup Table​ Data​ (8 bit version). + * @param value The value of Red Palette Color Lookup Table​ Data​ + * @param numEntries Number of entries in the lookup table + * @param checkValue Check value for conformance with VR (OW) and VM (1-n) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition + setBluePaletteColorLookupTableData(const Uint8* value, const unsigned long numEntries, const OFBool checkValue = OFTrue); + + /** Set the Segmented Red Palette Color Lookup Table​ Data​ (16 bit version). + * @param value The value of Red Palette Color Lookup Table​ Data​ + * @param numEntries Number of entries in the lookup table + * @param checkValue Check value for conformance with VR (OW) and VM (1-n) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setSegmentedRedPaletteColorLookupTableData(const Uint16* value, + const unsigned long numEntries, + const OFBool checkValue = OFTrue); + + /** Set the Segmented Red Palette Color Lookup Table​ Data​ (8 bit version). + * @param value The value of Red Palette Color Lookup Table​ Data​ + * @param numEntries Number of entries in the lookup table + * @param checkValue Check value for conformance with VR (OW) and VM (1-n) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setSegmentedRedPaletteColorLookupTableData(const Uint8* value, + const unsigned long numEntries, + const OFBool checkValue = OFTrue); + + /** Set the Segmented Green Palette Color Lookup Table​ Data​ (16 bit version). + * @param value The value of Red Palette Color Lookup Table​ Data​ + * @param numEntries Number of entries in the lookup table + * @param checkValue Check value for conformance with VR (OW) and VM (1-n) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setSegmentedGreenPaletteColorLookupTableData(const Uint16* value, + const unsigned long numEntries, + const OFBool checkValue = OFTrue); + + /** Set the Segmented Green Palette Color Lookup Table​ Data​ (8 bit version) + * @param value The value of Red Palette Color Lookup Table​ Data​ + * @param numEntries Number of entries in the lookup table + * @param checkValue Check value for conformance with VR (OW) and VM (1-n) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setSegmentedGreenPaletteColorLookupTableData(const Uint8* value, + const unsigned long numEntries, + const OFBool checkValue = OFTrue); + + /** Set the Segmented Blue Palette Color Lookup Table​ Data​ (16 bit version). + * @param value The value of Red Palette Color Lookup Table​ Data​ + * @param numEntries Number of entries in the lookup table + * @param checkValue Check value for conformance with VR (OW) and VM (1-n) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setSegmentedBluePaletteColorLookupTableData(const Uint16* value, + const unsigned long numEntries, + const OFBool checkValue = OFTrue); + + /** Set the Segmented Blue Palette Color Lookup Table​ Data​ (8 bit version). + * @param value The value of Red Palette Color Lookup Table​ Data​ + * @param numEntries Number of entries in the lookup table + * @param checkValue Check value for conformance with VR (OW) and VM (1-n) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setSegmentedBluePaletteColorLookupTableData(const Uint8* value, + const unsigned long numEntries, + const OFBool checkValue = OFTrue); + + // ---------------- Convenience Setters ----------------------------- + + /** Set the Palette Color Lookup Table​ Data​ (red, green, blue, 8 or 16 bit). + * @tparam T The data type of the color lookup table data. (Uint16 or Uint8) + * @param copyRed The value of Red Palette Color Lookup Table​ Data​ + * @param copyGreen The value of Green Palette Color Lookup Table​ Data​ + * @param copyBlue The value of Blue Palette Color Lookup Table​ Data​ + * @param numEntries Number of entries in each of the lookup tables + * @param checkValue Check value for conformance with VR (OW) and VM (1-n) if OFTrue + * @return EC_Normal if value is set, an error code otherwise + */ + template OFCondition setPaletteColorLookupTableData(const T* copyRed, + const T* copyGreen, + const T* copyBlue, + const unsigned long numEntries, + const OFBool checkValue = OFTrue); + + + /** Sets the red, green, and blue data for a segmented palette color lookup table. + * The data arrays for red, green, and blue should be of type T and have a length of numEntries. + * \tparam T The data type of the color lookup table data. (Uint16 or Uint8) + * \param copyRedData Pointer to the array containing the red data. + * \param copyGreenData Pointer to the array containing the green data. + * \param copyBlueData Pointer to the array containing the blue data. + * \param numEntries The number of entries in the lookup table. + * \param checkValue Flag indicating whether to check the input values for validity. Default is true. + * \return EC_Normal if successful, error otherwise. + */ + template OFCondition setSegmentedPaletteColorLookupTableData(const T* copyRedData, + const T* copyGreenData, + const T* copyBlueData, + const unsigned long numEntries, + const OFBool checkValue = OFTrue); + + /** Sets all three values of the Red Palette Color Lookup Table Descriptor. + * @param numEntries The number of entries in the lookup table. + * @param firstValueMapped The first value mapped in the lookup table. + * @param numBitsPerEntry The number of bits per entry in the lookup table. + * + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setRedPaletteColorLookupTableDescriptor(const Uint16 numEntries, + const Uint16 firstValueMapped, + const Uint8 numBitsPerEntry); + + /** Sets all three values of the Green Palette Color Lookup Table Descriptor. + * @param numEntries The number of entries in the lookup table. + * @param firstValueMapped The first value mapped in the lookup table. + * @param numBitsPerEntry The number of bits per entry in the lookup table. + * + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setGreenPaletteColorLookupTableDescriptor(const Uint16 numEntries, + const Uint16 firstValueMapped, + const Uint8 numBitsPerEntry); + + /** Sets the blue Palette Color Lookup Table Descriptor. + * @param numEntries The number of entries in the lookup table. + * @param firstValueMapped The first value mapped in the lookup table. + * @param numBitsPerEntry The number of bits per entry in the lookup table. + * + * @return EC_Normal if value is set, an error code otherwise + */ + virtual OFCondition setBluePaletteColorLookupTableDescriptor(const Uint16 numEntries, + const Uint16 firstValueMapped, + const Uint8 numBitsPerEntry); + +protected: + + /** Returns a copy of the data for the given tag. A copy of the data is returned into the given + * pointer and the number of entries is returned. + * @param dataTag The tag of the data to be copied (i.e. Red/Green/Blue/PaletteColorLookupTableData) + * @param lutData The pointer that will be set to the copied data + * @param num8BitEntries The number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getUint8DataCopy(const DcmTagKey& dataTag, const Uint8*& lutData, unsigned long& num8BitEntries); + + /** Put the given 8 bit data into the given tag. + * @param dataTag The tag of the data to be copied into (i.e. Red/Green/Blue/PaletteColorLookupTableData) + * @param lutData The data to be copied + * @param num8BitEntries The number of entries in the lookup table + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition putUint8Data(const DcmTagKey& dataTag, const Uint8* lutData, const unsigned long num8BitEntries); + + /** Returns a 16 bit copy of the data for the given tag. The data is returned into the given + * pointer and the number of entries is returned. + * @param dataTag The tag of the data to be copied to (i.e. Red/Green/Blue/PaletteColorLookupTableData) + * @param lutData The pointer that will be set to the copied data + * @param numEntries The number of entries in the data + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getUint16DataCopy(const DcmTagKey& dataTag, const Uint16*& lutData, unsigned long& numEntries); + + /** Returns a reference of the data from the given tag and the number of entries in the data + * @param dataTag The tag of the data to be copied to (i.e. Red/Green/Blue/PaletteColorLookupTableData) + * @param lutData The pointer that will be set to the data + * @param numEntries The number of entries in the data + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getUint16Data(const DcmTagKey& dataTag, const Uint16*& lutData, unsigned long& numEntries); + + /** Check given LUT for consistency with descriptor. + * @param descriptorTag The tag of the descriptor to be checked (i.e. Red/Green/BluePaletteColorLookupTableDescriptor) + * @param dataTag The tag of the data to be checked (i.e. Red/Green/BluePaletteColorLookupTableData). + * Should of course match the descriptor "color". + * @return OFTrue if LUT is consistent, OFFalse otherwise + */ + virtual OFBool checkLUT(const DcmTagKey& descriptorTag, + const DcmTagKey& dataTag); + + /** Check basic consistency rules of all palette descriptors (not their data). + * @param isError If OFTrue, errors are reported, otherwise warnings + * @return OFTrue if all descriptors are consistent, OFFalse otherwise + */ + virtual OFBool checkDescriptorConsistency(const OFBool& isError); + + /** Check basic consistency of all palette data with their descriptors. + * @param isError If OFTrue, errors are reported, otherwise warnings + * @return OFTrue if all data is consistent, OFFalse otherwise + */ + virtual OFBool checkDataConsistency(const OFBool& isError); + + /** Check that either segmented or unsegmented LUT data is used, not both. + * @param isError If OFTrue, errors are reported, otherwise warnings + * @param isSegmented Returns OFTrue if segmented LUT data is used, OFFalse otherwise + * @return OFTrue if LUT data is consistent, OFFalse otherwise + */ + virtual OFBool checkSegmentConsistency(const OFBool& isError, OFBool& isSegmented); + + /** Report error/warning for given descriptor tag. + * @param descriptorTag The tag of the descriptor to be reported (i.e. Red/Green/BluePaletteColorLookupTableDescriptor) + * @param message The message to be reported + * @param isError If OFTrue, an error is reported, otherwise a warning + */ + virtual void reportLUTError(const DcmTagKey& descriptorTag, + const OFString& message, + const OFBool& isError); + + /** Get number of LUT data entries for given descriptor tag. + * @param dataTag The tag of the data to be checked (i.e. Red/Green/BluePaletteColorLookupTableDescriptor) + * @param result The number of entries in the data + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition numEntriesForData(const DcmTagKey& dataTag, Uint16& result); + +private: + + /// The module's name ("PaletteColorLookupTableModule") + static const OFString m_ModuleName; + +}; + +#endif // MODPALETTECOLORLUT_H diff --git a/dcmiod/libsrc/CMakeLists.txt b/dcmiod/libsrc/CMakeLists.txt index 02686f54..1ea6b95b 100644 --- a/dcmiod/libsrc/CMakeLists.txt +++ b/dcmiod/libsrc/CMakeLists.txt @@ -21,10 +21,12 @@ DCMTK_ADD_LIBRARY(dcmiod modgeneralseries.cc modgeneralstudy.cc modhelp.cc + modiccprofile.cc modimagepixel.cc modimagepixelbase.cc modmultiframefg.cc modmultiframedimension.cc + modpalettecolorlut.cc modpatient.cc modpatientstudy.cc modsegmentationseries.cc @@ -33,5 +35,5 @@ DCMTK_ADD_LIBRARY(dcmiod modusfor.cc ) -DCMTK_TARGET_LINK_MODULES(dcmiod dcmdata ofstd oflog) +DCMTK_TARGET_LINK_MODULES(dcmiod dcmimgle dcmdata ofstd oflog) diff --git a/dcmiod/libsrc/Makefile.dep b/dcmiod/libsrc/Makefile.dep index 4d4b5eb0..dccf5c7c 100644 --- a/dcmiod/libsrc/Makefile.dep +++ b/dcmiod/libsrc/Makefile.dep @@ -46,6 +46,8 @@ iodcommn.o: iodcommn.cc ../../config/include/dcmtk/config/osconfig.h \ ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modcommoninstanceref.h \ @@ -59,7 +61,6 @@ iodcommn.o: iodcommn.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ @@ -161,7 +162,9 @@ iodcontentitemmacro.o: iodcontentitemmacro.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ ../include/dcmtk/dcmiod/iodrules.h ../include/dcmtk/dcmiod/iodtypes.h \ - ../include/dcmtk/dcmiod/ioddef.h ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ @@ -237,7 +240,9 @@ iodmacro.o: iodmacro.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ ../include/dcmtk/dcmiod/iodrules.h ../include/dcmtk/dcmiod/iodtypes.h \ - ../include/dcmtk/dcmiod/ioddef.h ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ @@ -327,8 +332,9 @@ iodreferences.o: iodreferences.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrui.h \ - ../include/dcmtk/dcmiod/iodtypes.h ../include/dcmtk/dcmiod/iodutil.h \ - ../include/dcmtk/dcmiod/iodrules.h \ + ../include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../include/dcmtk/dcmiod/iodutil.h ../include/dcmtk/dcmiod/iodrules.h \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h @@ -375,13 +381,14 @@ iodrules.o: iodrules.cc ../../config/include/dcmtk/config/osconfig.h \ ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ @@ -435,7 +442,8 @@ iodtypes.o: iodtypes.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def iodutil.o: iodutil.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmiod/iodutil.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ @@ -501,6 +509,7 @@ iodutil.o: iodutil.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ ../include/dcmtk/dcmiod/ioddef.h ../include/dcmtk/dcmiod/iodrules.h \ ../include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h \ @@ -558,6 +567,8 @@ modacquisitioncontext.o: modacquisitioncontext.cc \ ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ @@ -570,7 +581,6 @@ modacquisitioncontext.o: modacquisitioncontext.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ @@ -641,6 +651,7 @@ modbase.o: modbase.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ ../include/dcmtk/dcmiod/ioddef.h ../include/dcmtk/dcmiod/iodrules.h \ ../include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../include/dcmtk/dcmiod/iodutil.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ @@ -713,7 +724,9 @@ modcommoninstanceref.o: modcommoninstanceref.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ ../include/dcmtk/dcmiod/iodrules.h ../include/dcmtk/dcmiod/iodtypes.h \ - ../include/dcmtk/dcmiod/ioddef.h ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ @@ -776,6 +789,8 @@ modenhequipment.o: modenhequipment.cc \ ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ @@ -788,7 +803,6 @@ modenhequipment.o: modenhequipment.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ @@ -871,7 +885,9 @@ modenhusimage.o: modenhusimage.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ ../include/dcmtk/dcmiod/iodrules.h ../include/dcmtk/dcmiod/iodtypes.h \ - ../include/dcmtk/dcmiod/ioddef.h ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ @@ -954,7 +970,9 @@ modenhusseries.o: modenhusseries.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ ../include/dcmtk/dcmiod/iodrules.h ../include/dcmtk/dcmiod/iodtypes.h \ - ../include/dcmtk/dcmiod/ioddef.h ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ @@ -1014,6 +1032,8 @@ modequipment.o: modequipment.cc \ ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ @@ -1026,7 +1046,6 @@ modequipment.o: modequipment.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ @@ -1110,6 +1129,7 @@ modfloatingpointimagepixel.o: modfloatingpointimagepixel.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ ../include/dcmtk/dcmiod/ioddef.h ../include/dcmtk/dcmiod/iodrules.h \ ../include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h modfor.o: modfor.cc ../../config/include/dcmtk/config/osconfig.h \ @@ -1174,6 +1194,7 @@ modfor.o: modfor.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ ../include/dcmtk/dcmiod/ioddef.h ../include/dcmtk/dcmiod/iodrules.h \ ../include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlo.h \ @@ -1231,6 +1252,8 @@ modgeneralimage.o: modgeneralimage.cc \ ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ @@ -1243,7 +1266,6 @@ modgeneralimage.o: modgeneralimage.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ @@ -1333,6 +1355,7 @@ modgeneralseries.o: modgeneralseries.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ ../include/dcmtk/dcmiod/iodrules.h ../include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ @@ -1416,7 +1439,9 @@ modgeneralstudy.o: modgeneralstudy.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ ../include/dcmtk/dcmiod/iodrules.h ../include/dcmtk/dcmiod/iodtypes.h \ - ../include/dcmtk/dcmiod/ioddef.h ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ @@ -1496,6 +1521,83 @@ modhelp.o: modhelp.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h +modiccprofile.o: modiccprofile.cc \ + ../../config/include/dcmtk/config/osconfig.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../include/dcmtk/dcmiod/modiccprofile.h \ + ../include/dcmtk/dcmiod/iodrules.h ../include/dcmtk/dcmiod/iodtypes.h \ + ../include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../include/dcmtk/dcmiod/modbase.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ + ../include/dcmtk/dcmiod/iodutil.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ + ../../ofstd/include/dcmtk/ofstd/ofdate.h \ + ../../ofstd/include/dcmtk/ofstd/oftime.h \ + ../include/dcmtk/dcmiod/iccexample.h modimagepixel.o: modimagepixel.cc \ ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmiod/modimagepixel.h \ @@ -1561,6 +1663,7 @@ modimagepixel.o: modimagepixel.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ ../include/dcmtk/dcmiod/ioddef.h ../include/dcmtk/dcmiod/iodrules.h \ ../include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ @@ -1636,6 +1739,7 @@ modimagepixelbase.o: modimagepixelbase.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ ../include/dcmtk/dcmiod/ioddef.h ../include/dcmtk/dcmiod/iodrules.h \ ../include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ @@ -1691,6 +1795,8 @@ modmultiframedimension.o: modmultiframedimension.cc \ ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ @@ -1703,7 +1809,6 @@ modmultiframedimension.o: modmultiframedimension.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ @@ -1771,6 +1876,8 @@ modmultiframefg.o: modmultiframefg.cc \ ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ @@ -1783,7 +1890,6 @@ modmultiframefg.o: modmultiframefg.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ @@ -1803,6 +1909,88 @@ modmultiframefg.o: modmultiframefg.cc \ ../include/dcmtk/dcmiod/iodutil.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h +modpalettecolorlut.o: modpalettecolorlut.cc \ + ../../config/include/dcmtk/config/osconfig.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrui.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../include/dcmtk/dcmiod/iodtypes.h ../include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../include/dcmtk/dcmiod/modbase.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ + ../include/dcmtk/dcmiod/iodrules.h \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../include/dcmtk/dcmiod/modpalettecolorlut.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ + ../include/dcmtk/dcmiod/iodutil.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ + ../../ofstd/include/dcmtk/ofstd/ofdate.h \ + ../../ofstd/include/dcmtk/ofstd/oftime.h \ + ../../dcmimgle/include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmimgle/include/dcmtk/dcmimgle/dibaslut.h \ + ../../dcmimgle/include/dcmtk/dcmimgle/diutils.h \ + ../../dcmimgle/include/dcmtk/dcmimgle/didefine.h \ + ../../dcmimgle/include/dcmtk/dcmimgle/diobjcou.h modpatient.o: modpatient.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ @@ -1847,6 +2035,8 @@ modpatient.o: modpatient.cc ../../config/include/dcmtk/config/osconfig.h \ ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ @@ -1859,7 +2049,6 @@ modpatient.o: modpatient.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ @@ -1924,6 +2113,8 @@ modpatientstudy.o: modpatientstudy.cc \ ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modbase.h \ @@ -1936,7 +2127,6 @@ modpatientstudy.o: modpatientstudy.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ @@ -2020,6 +2210,7 @@ modsegmentationseries.o: modsegmentationseries.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ ../include/dcmtk/dcmiod/ioddef.h ../include/dcmtk/dcmiod/iodrules.h \ ../include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../include/dcmtk/dcmiod/iodutil.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ @@ -2094,6 +2285,7 @@ modsopcommon.o: modsopcommon.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ ../include/dcmtk/dcmiod/ioddef.h ../include/dcmtk/dcmiod/iodrules.h \ ../include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ @@ -2181,6 +2373,7 @@ modsynchronization.o: modsynchronization.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ ../include/dcmtk/dcmiod/ioddef.h ../include/dcmtk/dcmiod/iodrules.h \ ../include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h \ @@ -2248,6 +2441,7 @@ modusfor.o: modusfor.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ ../include/dcmtk/dcmiod/ioddef.h ../include/dcmtk/dcmiod/iodrules.h \ ../include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ diff --git a/dcmiod/libsrc/Makefile.in b/dcmiod/libsrc/Makefile.in index 14f20c0f..9d0820cc 100644 --- a/dcmiod/libsrc/Makefile.in +++ b/dcmiod/libsrc/Makefile.in @@ -15,8 +15,9 @@ include $(configdir)/@common_makefile@ ofstddir = $(top_srcdir)/../ofstd oflogdir = $(top_srcdir)/../oflog dcmdatadir = $(top_srcdir)/../dcmdata +dcmimgledir = $(top_srcdir)/../dcmimgle -LOCALINCLUDES = -I$(ofstddir)/include -I$(oflogdir)/include -I$(dcmdatadir)/include +LOCALINCLUDES = -I$(ofstddir)/include -I$(oflogdir)/include -I$(dcmdatadir)/include -I$(dcmimgledir)/include LOCALDEFS = @@ -26,7 +27,7 @@ objs = cielabutil.o iodcommn.o iodcontentitemmacro.o iodmacro.o iodreferences. modfloatingpointimagepixel.o modfor.o modgeneralimage.o modgeneralseries.o \ modgeneralstudy.o modhelp.o modimagepixelbase.o modimagepixel.o modmultiframedimension.o \ modmultiframefg.o modpatient.o modpatientstudy.o modsegmentationseries.o modsopcommon.o \ - modsynchronization.o modusfor.o + modsynchronization.o modusfor.o modiccprofile.o modpalettecolorlut.o library = libdcmiod.$(LIBEXT) diff --git a/dcmiod/libsrc/iodmacro.cc b/dcmiod/libsrc/iodmacro.cc index c0d7f994..f4c026f4 100644 --- a/dcmiod/libsrc/iodmacro.cc +++ b/dcmiod/libsrc/iodmacro.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -68,7 +68,9 @@ CodeSequenceMacro::CodeSequenceMacro(const OFString& codeValue, { // reset element rules resetRules(); - set(codeValue, codingSchemeDesignator, codeMeaning, codingSchemeVersion); + // We don't check the value here, since this is a convenience constructor and we + // cannot return an error code anyway + set(codeValue, codingSchemeDesignator, codeMeaning, codingSchemeVersion, OFFalse /* checkValue */); } OFCondition CodeSequenceMacro::check(const bool /* quiet */) @@ -96,7 +98,9 @@ CodeSequenceMacro::CodeSequenceMacro(OFshared_ptr item, { // reset element rules resetRules(); - set(codeValue, codingSchemeDesignator, codeMeaning, codingSchemeVersion); + // We don't check the value here, since this is a convenience constructor and we + // cannot return an error code anyway + set(codeValue, codingSchemeDesignator, codeMeaning, codingSchemeVersion, OFFalse /* checkValue */); } OFString CodeSequenceMacro::getName() const @@ -777,7 +781,10 @@ OFCondition ImageSOPInstanceReferenceMacro::create(const OFString& sopClassUID, { if (!refFramesOrSegments.empty()) { - if (sopClassUID == UID_SegmentationStorage) + if ( (sopClassUID == UID_SegmentationStorage) + || (sopClassUID == UID_LabelMapSegmentationStorage) + || (sopClassUID == UID_SurfaceSegmentationStorage) + || (sopClassUID == UID_HeightMapSegmentationStorage) ) { cond = result->setReferencedSegmentNumber(refFramesOrSegments); } @@ -1271,10 +1278,10 @@ ContentIdentificationMacro::ContentIdentificationMacro(const OFString& instanceN , m_IODRules() { resetRules(); - setInstanceNumber(instanceNumber); - setContentLabel(contentLabel); - setContentDescription(contentDescription); - setContentCreatorName(contentCreatorName); + setInstanceNumber(instanceNumber, OFFalse /* do not check value */); + setContentLabel(contentLabel, OFFalse /* do not check value */); + setContentDescription(contentDescription, OFFalse /* do not check value */); + setContentCreatorName(contentCreatorName, OFFalse /* do not check value */); } void ContentIdentificationMacro::resetRules() diff --git a/dcmiod/libsrc/iodrules.cc b/dcmiod/libsrc/iodrules.cc index 80f90c2b..4689f6c5 100644 --- a/dcmiod/libsrc/iodrules.cc +++ b/dcmiod/libsrc/iodrules.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -37,33 +37,46 @@ IODRules* IODRules::clone() IODRules* newRules = new IODRules(); if (newRules) { - OFMap::iterator it = m_Rules.begin(); - while (it != m_Rules.end()) + *newRules = *this; + } + else + { + DCMIOD_WARN("Cannot create new IODRules, memory exhausted?"); + } + return newRules; +} + +// Copy constructor +IODRules::IODRules(const IODRules& other) + : m_Rules() +{ + // use assignment operator + *this = other; +} + +// Assignment operator (deep copy) +IODRules& IODRules::operator=(const IODRules& other) +{ + if (this != &other) + { + clear(); // Clear existing rules + // Perform deep copy of all rules + OFMap::const_iterator it = other.m_Rules.begin(); + while (it != other.m_Rules.end()) { - if (it->second) + IODRule* newRule = it->second->clone(); + if (newRule) { - IODRule* newRule = it->second->clone(); - if (newRule) - { - newRules->addRule(newRule); - } - else - { - DCMIOD_WARN("Cannot create new IODRule, memory exhausted?"); - } + m_Rules.insert(OFMake_pair(it->first, newRule)); } else { - DCMIOD_WARN("Found NULL IODRule, cannot clone"); + DCMIOD_WARN("Cannot create new IODRule, memory exhausted?"); } - it++; + ++it; } } - else - { - DCMIOD_WARN("Cannot create new IODRules, memory exhausted?"); - } - return newRules; + return *this; } IODRule* IODRules::getByTag(const DcmTagKey& key) const @@ -160,7 +173,7 @@ void IODRules::dump(STD_NAMESPACE ostream& out) IODRules::iterator it = m_Rules.begin(); while (it != m_Rules.end()) { - out << (*it).first << ": Type \"" << (*it).second->getType() << "\", VM \"" << (*it).second->getType() << "\"" + out << (*it).first << ": Type \"" << (*it).second->getType() << "\", VM \"" << (*it).second->getType() << "\" Component: " << (*it).second->getModule() << OFendl; it++; } diff --git a/dcmiod/libsrc/iodtypes.cc b/dcmiod/libsrc/iodtypes.cc index ab2f8aa3..d4616cd0 100644 --- a/dcmiod/libsrc/iodtypes.cc +++ b/dcmiod/libsrc/iodtypes.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -46,3 +46,4 @@ makeOFConditionConst(IOD_EC_InvalidElementValue, OFM_dcmiod, 11, OF_error, "Valu makeOFConditionConst(IOD_EC_InvalidReference, OFM_dcmiod, 12, OF_error, "One or more invalid SOP references"); makeOFConditionConst( IOD_EC_ReferencesOmitted, OFM_dcmiod, 13, OF_error, "One or more SOP references have been omitted"); +makeOFConditionConst(IOD_EC_InvalidColorPalette, OFM_dcmiod, 14, OF_error, "Invalid Color Palette LUT"); diff --git a/dcmiod/libsrc/iodutil.cc b/dcmiod/libsrc/iodutil.cc index 3b6269d0..e8722147 100644 --- a/dcmiod/libsrc/iodutil.cc +++ b/dcmiod/libsrc/iodutil.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -23,7 +23,7 @@ #include "dcmtk/dcmiod/iodutil.h" #include "dcmtk/dcmdata/dctypes.h" // logger #include "dcmtk/dcmiod/iodrules.h" - +#include "dcmtk/dcmiod/iodtypes.h" #include "dcmtk/dcmdata/dcdeftag.h" #include "dcmtk/dcmdata/dcdicent.h" #include "dcmtk/dcmdata/dcdict.h" @@ -33,7 +33,6 @@ #include "dcmtk/dcmdata/dcuid.h" #include "dcmtk/dcmdata/dcvrda.h" #include "dcmtk/dcmdata/dcvrtm.h" -#include "dcmtk/dcmiod/iodtypes.h" #include "dcmtk/ofstd/ofstring.h" // --- static helpers --- @@ -197,7 +196,7 @@ DcmIODUtil::addElementToDataset(OFCondition& result, DcmItem& dataset, DcmElemen result, rule->getModule().c_str(), logLevel); - resetConditionIfCheckDisabled(result, checkValue, *delem); + resetValueCheckResult(result, checkValue, *delem); } if (result.good()) { @@ -691,7 +690,7 @@ Uint32 DcmIODUtil::limitMaxFrames(const size_t numFramesPresent, const OFString& OFCondition DcmIODUtil::extractBinaryFrames(Uint8* pixData, const size_t numFrames, const size_t bitsPerFrame, - OFVector& results) + OFVector& results) { if (pixData == NULL) { @@ -736,15 +735,15 @@ OFCondition DcmIODUtil::extractBinaryFrames(Uint8* pixData, return EC_MemoryExhausted; } memset(frameData, 0, bytesPerFrame); // Initialize to 0 - DcmIODTypes::Frame* frame = new DcmIODTypes::Frame(); + DcmIODTypes::Frame* frame = new DcmIODTypes::Frame(); if (frame == NULL) { DCMIOD_ERROR("Memory exhausted while extracting frames"); delete[] frameData; return EC_MemoryExhausted; } - frame->pixData = frameData; - frame->length = bytesPerFrame; + frame->m_pixData = frameData; + frame->m_numPixels = bytesPerFrame; results.push_back(frame); } @@ -762,7 +761,7 @@ OFCondition DcmIODUtil::extractBinaryFrames(Uint8* pixData, Uint8 bit = (pixData[inputByteIndex] >> (8 - bitsLeftInInputByte)) & 0x01; // Set bit in current frame to to position calculated from bitsLeftInTargetByte - results[targetFrameIndex]->pixData[targetByteIndex] |= (bit << (8 - bitsLeftInTargetByte)); + OFstatic_cast(Uint8*, results[targetFrameIndex]->getPixelData())[targetByteIndex] |= (bit << (8 - bitsLeftInTargetByte)); // Move to next bit bitsLeftInInputByte--; @@ -823,3 +822,33 @@ void DcmIODUtil::resetConditionIfCheckDisabled(OFCondition& result, const OFBool } } } + + +void DcmIODUtil::resetValueCheckResult(OFCondition& result, const OFBool checkValue, DcmElement& elem) +{ + if (!checkValue) + { + if ( (result == EC_ValueRepresentationViolated) || + (result == EC_MaximumLengthViolated) || + (result == EC_InvalidCharacter) || + (result == EC_ValueMultiplicityViolated) ) + { + // print element to string + OFOStringStream oss; + oss << elem.getTag() << " " << DcmVR(elem.getVR()).getVRName() << " "; + oss << DcmTag(elem.getTag()).getTagName() << " "; + if (elem.getLength() > 1024) + { + oss << "(value too long for printing)"; + } + { + OFString val; + elem.getOFString(val, 0, OFTrue); + oss << "[" << val << "]"; + } + + DCMIOD_DEBUG("Ignoring error (" << result.text() <<") when checking element: " << oss.str().c_str()); + result = EC_Normal; + } + } +} diff --git a/dcmiod/libsrc/modequipment.cc b/dcmiod/libsrc/modequipment.cc index ae114af9..b608b19a 100644 --- a/dcmiod/libsrc/modequipment.cc +++ b/dcmiod/libsrc/modequipment.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -105,6 +105,16 @@ OFCondition IODGeneralEquipmentModule::getSoftwareVersions(OFString& value, cons return DcmIODUtil::getStringValueFromItem(DCM_SoftwareVersions, *m_Item, value, pos); } +IODGeneralEquipmentModule::EquipmentInfo IODGeneralEquipmentModule::getEquipmentInfo() const +{ + EquipmentInfo info; + getManufacturer(info.m_Manufacturer); + getManufacturerModelName(info.m_ManufacturerModelName); + getDeviceSerialNumber(info.m_DeviceSerialNumber); + getSoftwareVersions(info.m_SoftwareVersions); + return info; +} + OFCondition IODGeneralEquipmentModule::setDeviceSerialNumber(const OFString& value, const OFBool checkValue) { OFCondition result = (checkValue) ? DcmLongString::checkStringValue(value, "1") : EC_Normal; diff --git a/dcmiod/libsrc/modhelp.cc b/dcmiod/libsrc/modhelp.cc index 7aafb00d..7d2f00d0 100644 --- a/dcmiod/libsrc/modhelp.cc +++ b/dcmiod/libsrc/modhelp.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -39,7 +39,7 @@ const DcmTagKey DcmModuleHelpers::patientModuleTags[] = { DCM_PatientName, DCM_RETIRED_OtherPatientIDs, DCM_OtherPatientIDsSequence, DCM_OtherPatientNames, - DCM_EthnicGroup, + DCM_RETIRED_EthnicGroup, DCM_PatientComments, DCM_PatientSpeciesDescription, DCM_PatientSpeciesCodeSequence, diff --git a/dcmiod/libsrc/modiccprofile.cc b/dcmiod/libsrc/modiccprofile.cc new file mode 100644 index 00000000..75907c62 --- /dev/null +++ b/dcmiod/libsrc/modiccprofile.cc @@ -0,0 +1,152 @@ +/* + * + * Copyright (C) 2024-2025, Open Connections GmbH + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation are maintained by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmiod + * + * Author: Michael Onken + * + * Purpose: Class for managing the ICC Profile Module + * + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ + +#include "dcmtk/ofstd/ofcast.h" +#include "dcmtk/ofstd/ofcond.h" +#include "dcmtk/dcmdata/dcdeftag.h" +#include "dcmtk/dcmdata/dcerror.h" +#include "dcmtk/dcmdata/dcvrobow.h" +#include "dcmtk/dcmdata/dcvrcs.h" +#include "dcmtk/dcmiod/modiccprofile.h" +#include "dcmtk/dcmiod/iodutil.h" +#include "dcmtk/dcmiod/iodrules.h" +#include "dcmtk/dcmiod/iccexample.h" + +const OFString IODICCProfileModule::m_ModuleName = "ICCProfileModule"; + +IODICCProfileModule::IODICCProfileModule(OFshared_ptr item, OFshared_ptr rules) + : IODModule(item, rules) +{ + // reset element rules + resetRules(); +} + +IODICCProfileModule::IODICCProfileModule() + : IODModule() +{ + resetRules(); +} + + +IODICCProfileModule::~IODICCProfileModule() +{ + // nothing to do +} + +void IODICCProfileModule::resetRules() +{ + m_Rules->addRule(new IODRule(DCM_ICCProfile, "1", "1", getName(), DcmIODTypes::IE_IMAGE),OFTrue); + m_Rules->addRule(new IODRule(DCM_ColorSpace, "1", "3", getName(), DcmIODTypes::IE_IMAGE),OFTrue); +} + +void IODICCProfileModule::clearData() +{ + IODModule::clearData(); +} + +OFString IODICCProfileModule::getName() const +{ + return m_ModuleName; +} + +OFCondition IODICCProfileModule::read(DcmItem& source, const OFBool clearOldData) +{ + if (clearOldData) + clearData(); + + IODComponent::read(source, OFFalse /* data already cleared */); + return EC_Normal; +} + +OFCondition IODICCProfileModule::write(DcmItem& destination) +{ + return IODComponent::write(destination); +} + +// --- get() functionality --- + +OFCondition IODICCProfileModule::getICCProfile(const Uint8*& value, Uint32& numBytes) +{ + DcmElement* elem = NULL; + OFCondition result = DcmIODUtil::getAndCheckElementFromDataset(*m_Item, elem, getRules()->getByTag(DCM_ICCProfile)); + if (result.good()) + { + if (elem->ident() == EVR_OB) + + { + Uint8* val = NULL; + result = OFstatic_cast(DcmOtherByteOtherWord*, elem)->getUint8Array(val); + if (result.good()) + { + // detach value pointer and set numBytes + value = val; + numBytes = elem->getLength(); + elem->detachValueField(); + } + } + else + { + result = EC_InvalidVR; + } + } + delete elem; // clean up, we copied the value already + return result; +} + +OFCondition IODICCProfileModule::getColorSpace(OFString& value, const signed long pos) const +{ + return DcmIODUtil::getStringValueFromItem(DCM_ColorSpace, *m_Item, value, pos); +} + +// --- set() functionality --- + +OFCondition IODICCProfileModule::setICCProfile(const Uint8* value, const unsigned long numBytes, const OFBool /* not used */) +{ + return m_Item->putAndInsertUint8Array(DCM_ICCProfile, value, numBytes); +} + +OFCondition IODICCProfileModule::setDefaultProfile(const OFBool& setColorSpaceDescription) +{ + OFCondition result = setICCProfile(DCMTK_SRGB_ICC_SAMPLE, DCMTK_SRGB_ICC_SAMPLE_LEN, OFFalse /* do not check, we expect this is valid */); + if (result.good() && setColorSpaceDescription) + { + result = setColorSpace("SRGB", OFFalse /* do not check, we expect this is valid */); + } + return result; +} + +OFCondition IODICCProfileModule::setColorSpace(const OFString& value, const OFBool checkValue) +{ + OFCondition result; + IODRule* rule = getRules()->getByTag(DCM_ColorSpace); + if (rule) + { + result = (checkValue) ? DcmCodeString::checkStringValue(value, rule->getVM()) : EC_Normal; + if (result.good()) result = m_Item->putAndInsertOFStringArray(DCM_ColorSpace, value); + } + else + { + result = IOD_EC_NoSuchRule; + } + return result; +} diff --git a/dcmiod/libsrc/modmultiframedimension.cc b/dcmiod/libsrc/modmultiframedimension.cc index 28e0f3da..f65d8f15 100644 --- a/dcmiod/libsrc/modmultiframedimension.cc +++ b/dcmiod/libsrc/modmultiframedimension.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -46,6 +46,7 @@ IODMultiframeDimensionModule::IODMultiframeDimensionModule() : IODModule() , m_DimensionIndexSequence() , m_DimensionOrganizationSequence() + , m_CheckOnWrite(OFTrue) { resetRules(); } @@ -200,8 +201,8 @@ OFCondition IODMultiframeDimensionModule::checkDimensions(DcmItem* fgItem) DcmSequenceOfItems* perFrame = NULL; if (fgItem->findAndGetSequence(DCM_PerFrameFunctionalGroupsSequence, perFrame).bad()) { - DCMIOD_WARN( - "Will not check dimension consistency with functional groups (no per-frame functional groups found)"); + DCMIOD_DEBUG( + "Omitting dimension consistency check with functional groups (Per-frame FGs not yet present)"); } OFCondition result; diff --git a/dcmiod/libsrc/modpalettecolorlut.cc b/dcmiod/libsrc/modpalettecolorlut.cc new file mode 100644 index 00000000..f963ae1c --- /dev/null +++ b/dcmiod/libsrc/modpalettecolorlut.cc @@ -0,0 +1,985 @@ +/* + * + * Copyright (C) 2024-2025, Open Connections GmbH + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation are maintained by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmiod + * + * Author: Michael Onken + * + * Purpose: Class for managing the Palette Color LUT Module + * + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ + +#include "dcmtk/dcmdata/dcelem.h" +#include "dcmtk/dcmdata/dcerror.h" +#include "dcmtk/dcmdata/dcvrobow.h" +#include "dcmtk/dcmdata/dcvrui.h" +#include "dcmtk/dcmiod/iodtypes.h" +#include "dcmtk/dcmiod/modbase.h" +#include "dcmtk/dcmiod/modpalettecolorlut.h" +#include "dcmtk/ofstd/ofcond.h" +#include "dcmtk/dcmdata/dcdeftag.h" +#include "dcmtk/dcmdata/dcvrus.h" +#include "dcmtk/dcmiod/iodutil.h" +#include "dcmtk/dcmimgle/diluptab.h" +#include "dcmtk/ofstd/ofmem.h" +#include "dcmtk/ofstd/oftypes.h" + +const OFString IODPaletteColorLUTModule::m_ModuleName = "PaletteColorLookupTableModule"; + +IODPaletteColorLUTModule::IODPaletteColorLUTModule(OFshared_ptr item, OFshared_ptr rules) + : IODModule(item, rules) +{ + // reset element rules + resetRules(); +} + +IODPaletteColorLUTModule::IODPaletteColorLUTModule() + : IODModule() +{ + resetRules(); +} + + +IODPaletteColorLUTModule::~IODPaletteColorLUTModule() +{ + // nothing to do +} + +void IODPaletteColorLUTModule::resetRules() +{ + m_Rules->addRule(new IODRule(DCM_RedPaletteColorLookupTableDescriptor, "3", "1", getName(), DcmIODTypes::IE_IMAGE), + OFTrue); + m_Rules->addRule( + new IODRule(DCM_GreenPaletteColorLookupTableDescriptor, "3", "1", getName(), DcmIODTypes::IE_IMAGE), OFTrue); + m_Rules->addRule(new IODRule(DCM_BluePaletteColorLookupTableDescriptor, "3", "1", getName(), DcmIODTypes::IE_IMAGE), + OFTrue); + m_Rules->addRule(new IODRule(DCM_PaletteColorLookupTableUID, "1", "3", getName(), DcmIODTypes::IE_IMAGE), OFTrue); + m_Rules->addRule(new IODRule(DCM_RedPaletteColorLookupTableData, "1", "1C", getName(), DcmIODTypes::IE_IMAGE), + OFTrue); + m_Rules->addRule(new IODRule(DCM_GreenPaletteColorLookupTableData, "1", "1C", getName(), DcmIODTypes::IE_IMAGE), + OFTrue); + m_Rules->addRule(new IODRule(DCM_BluePaletteColorLookupTableData, "1", "1C", getName(), DcmIODTypes::IE_IMAGE), + OFTrue); + m_Rules->addRule( + new IODRule(DCM_SegmentedRedPaletteColorLookupTableData, "1", "1C", getName(), DcmIODTypes::IE_IMAGE), OFTrue); + m_Rules->addRule( + new IODRule(DCM_SegmentedGreenPaletteColorLookupTableData, "1", "1C", getName(), DcmIODTypes::IE_IMAGE), + OFTrue); + m_Rules->addRule( + new IODRule(DCM_SegmentedBluePaletteColorLookupTableData, "1", "1C", getName(), DcmIODTypes::IE_IMAGE), OFTrue); +} + +void IODPaletteColorLUTModule::clearData() +{ + IODModule::clearData(); +} + +OFString IODPaletteColorLUTModule::getName() const +{ + return m_ModuleName; +} + +OFCondition IODPaletteColorLUTModule::read(DcmItem& source, const OFBool clearOldData) +{ + if (clearOldData) + clearData(); + + IODComponent::read(source, OFFalse /* data already cleared */); + OFBool isSegmented; + checkSegmentConsistency(OFFalse, isSegmented); + if (!isSegmented) + { + checkLUT(DCM_RedPaletteColorLookupTableDescriptor, DCM_RedPaletteColorLookupTableData); + checkLUT(DCM_GreenPaletteColorLookupTableDescriptor, DCM_GreenPaletteColorLookupTableData); + checkLUT(DCM_BluePaletteColorLookupTableDescriptor, DCM_BluePaletteColorLookupTableData); + checkDescriptorConsistency(OFFalse /* only warn */); + checkDataConsistency(OFFalse /* only warn */); + } + return EC_Normal; +} + +OFCondition IODPaletteColorLUTModule::write(DcmItem& destination) +{ + OFBool valid, isSegmented; + valid = checkSegmentConsistency(OFFalse, isSegmented); + if (valid) + { + if (!isSegmented) + { + valid = checkLUT(DCM_RedPaletteColorLookupTableDescriptor, DCM_RedPaletteColorLookupTableData); + if (valid) + valid = checkLUT(DCM_GreenPaletteColorLookupTableDescriptor, DCM_GreenPaletteColorLookupTableData); + if (valid) + valid = checkLUT(DCM_BluePaletteColorLookupTableDescriptor, DCM_BluePaletteColorLookupTableData); + if (valid) + valid = checkDescriptorConsistency(OFTrue /* report as errors */); + if (valid) + valid = checkDataConsistency(OFTrue /* report as errors */); + if (valid) + { + return IODComponent::write(destination); + } + else + { + return IOD_EC_InvalidColorPalette; + } + } + else + { + return IODComponent::write(destination); + } + } + + return IOD_EC_InvalidColorPalette; +} + +OFCondition IODPaletteColorLUTModule::getRedPaletteColorLookupTableDescriptor(Uint16& value, + const unsigned long pos) const +{ + OFCondition result = m_Item->findAndGetUint16(DCM_RedPaletteColorLookupTableDescriptor, value, pos); + return result; +} + +OFCondition IODPaletteColorLUTModule::getGreenPaletteColorLookupTableDescriptor(Uint16& value, + const unsigned long pos) const +{ + OFCondition result = m_Item->findAndGetUint16(DCM_GreenPaletteColorLookupTableDescriptor, value, pos); + return result; +} + +OFCondition IODPaletteColorLUTModule::getBluePaletteColorLookupTableDescriptor(Uint16& value, + const unsigned long pos) const +{ + OFCondition result = m_Item->findAndGetUint16(DCM_BluePaletteColorLookupTableDescriptor, value, pos); + return result; +} + + +OFCondition IODPaletteColorLUTModule::getPaletteColorLookupTableUID(OFString& value, const signed long pos) const +{ + return DcmIODUtil::getStringValueFromItem(DCM_PaletteColorLookupTableUID, *m_Item, value, pos); +} + +OFCondition IODPaletteColorLUTModule::getRedPaletteColorLookupTableData(const Uint16*& dataCopy, unsigned long& numEntries) +{ + return getUint16DataCopy(DCM_RedPaletteColorLookupTableData, dataCopy, numEntries); +} + +OFCondition IODPaletteColorLUTModule::getGreenPaletteColorLookupTableData(const Uint16*& dataCopy, unsigned long& numEntries) +{ + return getUint16DataCopy(DCM_GreenPaletteColorLookupTableData, dataCopy, numEntries); +} + +OFCondition IODPaletteColorLUTModule::getBluePaletteColorLookupTableData(const Uint16*& dataCopy, unsigned long& numEntries) +{ + return getUint16DataCopy(DCM_BluePaletteColorLookupTableData, dataCopy, numEntries); +} + +OFCondition IODPaletteColorLUTModule::getSegmentedRedPaletteColorLookupTableData(const Uint16*& dataCopy, unsigned long& numEntries) +{ + return getUint16DataCopy(DCM_SegmentedRedPaletteColorLookupTableData, dataCopy, numEntries); +} + +OFCondition IODPaletteColorLUTModule::getSegmentedGreenPaletteColorLookupTableData(const Uint16*& dataCopy, unsigned long& numEntries) +{ + return getUint16DataCopy(DCM_SegmentedGreenPaletteColorLookupTableData, dataCopy, numEntries); +} + +OFCondition IODPaletteColorLUTModule::getSegmentedBluePaletteColorLookupTableData(const Uint16*& dataCopy, unsigned long& numEntries) +{ + return getUint16DataCopy(DCM_SegmentedBluePaletteColorLookupTableData, dataCopy, numEntries); +} + +// 8 bit versions for LUT data access + +OFCondition IODPaletteColorLUTModule::getRedPaletteColorLookupTableData(const Uint8*& dataCopy, unsigned long& numEntries) +{ + return getUint8DataCopy(DCM_RedPaletteColorLookupTableData, dataCopy, numEntries); +} + +OFCondition IODPaletteColorLUTModule::getGreenPaletteColorLookupTableData(const Uint8*& dataCopy, unsigned long& numEntries) +{ + return getUint8DataCopy(DCM_GreenPaletteColorLookupTableData, dataCopy, numEntries); +} + +OFCondition IODPaletteColorLUTModule::getBluePaletteColorLookupTableData(const Uint8*& dataCopy, unsigned long& numEntries) +{ + return getUint8DataCopy(DCM_BluePaletteColorLookupTableData, dataCopy, numEntries); +} + +OFCondition IODPaletteColorLUTModule::getSegmentedRedPaletteColorLookupTableData(const Uint8*& dataCopy, unsigned long& numEntries) +{ + return getUint8DataCopy(DCM_SegmentedRedPaletteColorLookupTableData, dataCopy, numEntries); +} + +OFCondition IODPaletteColorLUTModule::getSegmentedGreenPaletteColorLookupTableData(const Uint8*& dataCopy, unsigned long& numEntries) +{ + return getUint8DataCopy(DCM_SegmentedGreenPaletteColorLookupTableData, dataCopy, numEntries); +} + +OFCondition IODPaletteColorLUTModule::getSegmentedBluePaletteColorLookupTableData(const Uint8*& dataCopy, unsigned long& numEntries) +{ + return getUint8DataCopy(DCM_SegmentedBluePaletteColorLookupTableData, dataCopy, numEntries); +} + +// -------------------- set() -------------------- + +OFCondition IODPaletteColorLUTModule::setRedPaletteColorLookupTableDescriptor(const Uint16& value, + const unsigned long pos) +{ + OFCondition result = m_Item->putAndInsertUint16(DCM_RedPaletteColorLookupTableDescriptor, value, pos); + return result; +} + + +OFCondition IODPaletteColorLUTModule::setGreenPaletteColorLookupTableDescriptor(const Uint16& value, + const unsigned long pos) +{ + return m_Item->putAndInsertUint16(DCM_GreenPaletteColorLookupTableDescriptor, value, pos); +} + + +OFCondition IODPaletteColorLUTModule::setBluePaletteColorLookupTableDescriptor(const Uint16& value, + const unsigned long pos) +{ + return m_Item->putAndInsertUint16(DCM_BluePaletteColorLookupTableDescriptor, value, pos); +} + + +OFCondition IODPaletteColorLUTModule::setPaletteColorLookupTableUID(const OFString& value, const OFBool checkValue) +{ + OFCondition result = (checkValue) ? DcmUniqueIdentifier::checkStringValue(value, "1") : EC_Normal; + if (result.good()) result = m_Item->putAndInsertOFStringArray(DCM_PaletteColorLookupTableUID, value); + return result; +} + +OFCondition IODPaletteColorLUTModule::setRedPaletteColorLookupTableData(const Uint16* data, + const unsigned long numEntries, + const OFBool checkValue) +{ + if (checkValue && (numBits() == 8)) + { + DCMIOD_ERROR("Cannot set 16 bit data for 8 bit LUT"); + return IOD_EC_InvalidColorPalette; + } + unsigned long fixZero = numEntries; + if (numEntries == 0) + { + fixZero = 65536; + } + return m_Item->putAndInsertUint16Array(DCM_RedPaletteColorLookupTableData, data, fixZero); +} + +OFCondition IODPaletteColorLUTModule::setGreenPaletteColorLookupTableData(const Uint16* data, + const unsigned long numEntries, + const OFBool checkValue) +{ + if (checkValue && (numBits() == 8)) + { + DCMIOD_ERROR("Cannot set 16 bit data for 8 bit LUT"); + return IOD_EC_InvalidColorPalette; + } + unsigned long fixZero = numEntries; + if (numEntries == 0) + { + fixZero = 65536; + } + return m_Item->putAndInsertUint16Array(DCM_GreenPaletteColorLookupTableData, data, fixZero); +} + +OFCondition IODPaletteColorLUTModule::setBluePaletteColorLookupTableData(const Uint16* data, + const unsigned long numEntries, + const OFBool checkValue) +{ + if (checkValue && (numBits() == 8)) + { + DCMIOD_ERROR("Cannot set 16 bit data for 8 bit LUT"); + return IOD_EC_InvalidColorPalette; + } + unsigned long zeroFix = numEntries; + if (numEntries == 0) + { + zeroFix = 65536; + } + return m_Item->putAndInsertUint16Array(DCM_BluePaletteColorLookupTableData, data, zeroFix); +} + +OFCondition IODPaletteColorLUTModule::setRedPaletteColorLookupTableData(const Uint8* data, + const unsigned long numEntries, + const OFBool checkValue) +{ + if (checkValue && (numBits() == 16)) + { + DCMIOD_ERROR("Cannot set 8 bit data for 16 bit LUT"); + return IOD_EC_InvalidColorPalette; + } + unsigned long zeroFix = numEntries; + if (numEntries == 0) + { + zeroFix = 256; + } + return putUint8Data(DCM_RedPaletteColorLookupTableData, data, zeroFix); +} + +OFCondition IODPaletteColorLUTModule::setGreenPaletteColorLookupTableData(const Uint8* data, + const unsigned long numEntries, + const OFBool checkValue) +{ + if (checkValue && (numBits() == 16)) + { + DCMIOD_ERROR("Cannot set 8 bit data for 16 bit LUT"); + return IOD_EC_InvalidColorPalette; + } + unsigned long zeroFix = numEntries; + if (numEntries == 0) + { + zeroFix = 256; + } + return putUint8Data(DCM_GreenPaletteColorLookupTableData, data, zeroFix); +} + +OFCondition IODPaletteColorLUTModule::setBluePaletteColorLookupTableData(const Uint8* data, + const unsigned long numEntries, + const OFBool checkValue) +{ + if (checkValue && (numBits() == 16)) + { + DCMIOD_ERROR("Cannot set 8 bit data for 16 bit LUT"); + return IOD_EC_InvalidColorPalette; + } + unsigned long zeroFix = numEntries; + if (numEntries == 0) + { + zeroFix = 256; + } + return putUint8Data(DCM_BluePaletteColorLookupTableData, data, zeroFix); +} + +OFCondition IODPaletteColorLUTModule::setSegmentedRedPaletteColorLookupTableData(const Uint16* data, + const unsigned long numEntries, + const OFBool checkValue) +{ + if (checkValue && (numBits() == 8)) + { + DCMIOD_ERROR("Cannot set 16 bit data for 8 bit LUT"); + return IOD_EC_InvalidColorPalette; + } + unsigned long zeroFix = numEntries; + if (numEntries == 0) + { + zeroFix = 65536; + } + return m_Item->putAndInsertUint16Array(DCM_SegmentedRedPaletteColorLookupTableData, data, zeroFix); +} + +OFCondition IODPaletteColorLUTModule::setSegmentedGreenPaletteColorLookupTableData(const Uint16* data, + const unsigned long numEntries, + const OFBool checkValue) +{ + if (checkValue && (numBits() == 8)) + { + DCMIOD_ERROR("Cannot set 16 bit data for 8 bit LUT"); + return IOD_EC_InvalidColorPalette; + } + unsigned long zeroFix = numEntries; + if (numEntries == 0) + { + zeroFix = 65536; + } + return m_Item->putAndInsertUint16Array(DCM_SegmentedGreenPaletteColorLookupTableData, data, zeroFix); +} + +OFCondition IODPaletteColorLUTModule::setSegmentedBluePaletteColorLookupTableData(const Uint16* data, + const unsigned long numEntries, + const OFBool checkValue) +{ + if (checkValue && (numBits() == 8)) + { + DCMIOD_ERROR("Cannot set 16 bit data for 8 bit LUT"); + return IOD_EC_InvalidColorPalette; + } + unsigned long zeroFix = numEntries; + if (numEntries == 0) + { + zeroFix = 65536; + } + return m_Item->putAndInsertUint16Array(DCM_SegmentedBluePaletteColorLookupTableData, data, zeroFix); +} + +OFCondition IODPaletteColorLUTModule::setSegmentedRedPaletteColorLookupTableData(const Uint8* data, + const unsigned long numEntries, + const OFBool checkValue) +{ + if (checkValue && (numBits() == 16)) + { + DCMIOD_ERROR("Cannot set 8 bit data for 16 bit LUT"); + return IOD_EC_InvalidColorPalette; + } + unsigned long zeroFix = numEntries; + if (numEntries == 0) + { + zeroFix = 256; + } + return putUint8Data(DCM_SegmentedRedPaletteColorLookupTableData, data, zeroFix); +} + +OFCondition IODPaletteColorLUTModule::setSegmentedGreenPaletteColorLookupTableData(const Uint8* data, + const unsigned long numEntries, + const OFBool checkValue) +{ + if (checkValue && (numBits() == 16)) + { + DCMIOD_ERROR("Cannot set 8 bit data for 16 bit LUT"); + return IOD_EC_InvalidColorPalette; + } + unsigned long zeroFix = numEntries; + if (numEntries == 0) + { + zeroFix = 256; + } + return putUint8Data(DCM_SegmentedGreenPaletteColorLookupTableData, data, zeroFix); +} + +OFCondition IODPaletteColorLUTModule::setSegmentedBluePaletteColorLookupTableData(const Uint8* data, + const unsigned long numEntries, + const OFBool checkValue) +{ + if (checkValue && (numBits() == 16)) + { + DCMIOD_ERROR("Cannot set 8 bit data for 16 bit LUT"); + return IOD_EC_InvalidColorPalette; + } + unsigned long zeroFix = numEntries; + if (numEntries == 0) + { + zeroFix = 256; + } + return putUint8Data(DCM_SegmentedBluePaletteColorLookupTableData, data, zeroFix); +} + + +template +OFCondition IODPaletteColorLUTModule::setPaletteColorLookupTableData(const T* redData, + const T* greenData, + const T* blueData, + const unsigned long numEntries, + const OFBool checkValue) +{ + OFCondition result = setRedPaletteColorLookupTableData(redData, numEntries, checkValue); + if (result.good()) result = setGreenPaletteColorLookupTableData(greenData, numEntries, checkValue); + if (result.good()) result = setBluePaletteColorLookupTableData(blueData, numEntries, checkValue); + return result; +} + +template +OFCondition IODPaletteColorLUTModule::setSegmentedPaletteColorLookupTableData(const T* redData, + const T* greenData, + const T* blueData, + const unsigned long numEntries, + const OFBool checkValue) +{ + OFCondition result = setSegmentedRedPaletteColorLookupTableData(redData, numEntries, checkValue); + if (result.good()) result = setSegmentedGreenPaletteColorLookupTableData(greenData, numEntries, checkValue); + if (result.good()) result = setSegmentedBluePaletteColorLookupTableData(blueData, numEntries, checkValue); + return result; +} + +OFCondition IODPaletteColorLUTModule::setRedPaletteColorLookupTableDescriptor(const Uint16 numEntries, + const Uint16 firstMapped, + const Uint8 bitsPerEntry) +{ + // TODO Check value + OFCondition result; + Uint16 values[3]; + values[0] = numEntries; + values[1] = firstMapped; + values[2] = bitsPerEntry; + + return m_Item->putAndInsertUint16Array(DCM_RedPaletteColorLookupTableDescriptor, values, 3); +} + + +OFCondition IODPaletteColorLUTModule::setGreenPaletteColorLookupTableDescriptor(const Uint16 numEntries, + const Uint16 firstMapped, + const Uint8 bitsPerEntry) +{ + // TODO check value + OFCondition result; + Uint16 values[3]; + values[0] = numEntries; + values[1] = firstMapped; + values[2] = bitsPerEntry; + return m_Item->putAndInsertUint16Array(DCM_GreenPaletteColorLookupTableDescriptor, values, 3); +} + + + +OFCondition IODPaletteColorLUTModule::setBluePaletteColorLookupTableDescriptor(const Uint16 numEntries, + const Uint16 firstMapped, + const Uint8 bitsPerEntry) +{ + OFCondition result; + Uint16 values[3]; + values[0] = numEntries; + values[1] = firstMapped; + values[2] = bitsPerEntry; + + return m_Item->putAndInsertUint16Array(DCM_BluePaletteColorLookupTableDescriptor, values, 3); +} + + +OFBool IODPaletteColorLUTModule::checkLUT(const DcmTagKey& descriptorTag, + const DcmTagKey& dataTag) +{ + DcmElement* data = NULL; + DcmElement* descriptor = NULL; + m_Item->findAndGetElement(dataTag, data); + m_Item->findAndGetElement(descriptorTag, descriptor); + if (!data || !descriptor) + { + reportLUTError(descriptorTag, "Palette Color LUT Data or Descriptor missing", OFFalse /* warning */); + return OFFalse; + } + // LUT Data always OW in this module, LUT Descriptor is always US or SS + if ( (data->getTag().getEVR() != EVR_OW) || ( (descriptor->getTag().getEVR() != EVR_US) && (descriptor->getTag().getEVR() != EVR_SS)) ) + { + reportLUTError(descriptorTag, "Palette Color LUT Data or Descriptor have invalid VR", OFFalse /* warning */); + return OFFalse; + } + DiLookupTable lut(*OFstatic_cast(DcmOtherByteOtherWord*, data), *OFstatic_cast(DcmUnsignedShort*, descriptor), NULL, ELM_CheckValue); + if (!lut.isValid()) + { + reportLUTError(descriptorTag, "Palette Color LUT Data is invalid", OFFalse /* warning */); + return OFFalse; + } + return OFTrue; +} + + +void IODPaletteColorLUTModule::reportLUTError(const DcmTagKey& tag, const OFString& message, const OFBool& warning) +{ + OFString color; + if (tag == DCM_RedPaletteColorLookupTableDescriptor) + color = "Red"; + else if (tag == DCM_GreenPaletteColorLookupTableDescriptor) + color = "Green"; + else if (tag == DCM_BluePaletteColorLookupTableDescriptor) + color = "Blue"; + if (warning) + DCMIOD_WARN(color << " " << message); + else + DCMIOD_ERROR(color << " " << message); +} + +Uint8 IODPaletteColorLUTModule::numBits() +{ + OFCondition result; + Uint16 numBitsR = 0; + Uint16 numBitsG = 0; + Uint16 numBitsB = 0; + if (getRedPaletteColorLookupTableDescriptor(numBitsR, 2).good() && getGreenPaletteColorLookupTableDescriptor(numBitsG, 2).good() && getBluePaletteColorLookupTableDescriptor(numBitsB, 2).good()) + { + if ((numBitsR != numBitsG) || (numBitsR != numBitsB) || (numBitsG != numBitsB)) + { + DCMIOD_WARN("Bits per entry in Red, Green and Blue Palette Color LUT Descriptor are not equal"); + return 0; + } + } + else + { + DCMIOD_WARN("Could not read bits per entry in Red, Green or Blue Palette Color LUT Descriptor"); + return 0; + } + if ((numBitsR != 8) && (numBitsR != 16)) + { + DCMIOD_WARN("Bits per entry in Red, Green and Blue Palette Color LUT Descriptor are greater than 16"); + return 0; + } + return OFstatic_cast(Uint8, numBitsR); +} + + +OFBool IODPaletteColorLUTModule::checkDescriptorConsistency(const OFBool& isError) +{ + // Check whether all 2nd values ("first value mapped") have same value. + Uint16 redFirstMapped = 0; + Uint16 greenFirstMapped = 0; + Uint16 blueFirstMapped = 0; + Sint32 r,g,b; + r=g=b=0; + if (getRedPaletteColorLookupTableDescriptor(redFirstMapped, 1).good()) + { + if (getGreenPaletteColorLookupTableDescriptor(greenFirstMapped, 1).good()) + { + if (getBluePaletteColorLookupTableDescriptor(blueFirstMapped, 1).good()) + { + r = redFirstMapped; + g = greenFirstMapped; + b = blueFirstMapped; + if ( (r != g) || (r != b) || (g != b) ) + { + if (isError) + { + DCMIOD_ERROR("First value mapped in Red, Green and Blue Palette Color LUT Descriptor are not equal"); + } + else + { + DCMIOD_WARN("First value mapped in Red, Green and Blue Palette Color LUT Descriptor are not equal"); + } + return OFFalse; + } + Uint16 lastRed, lastGreen, lastBlue; + lastRed = lastGreen = lastBlue = 0; + if (getRedPaletteColorLookupTableDescriptor(lastRed, 2).good() && getGreenPaletteColorLookupTableDescriptor(lastGreen, 2).good() && getBluePaletteColorLookupTableDescriptor(lastBlue, 2).good()) + { + if (lastRed != lastGreen || lastRed != lastBlue || lastGreen != lastBlue) + { + if (isError) + { + DCMIOD_ERROR("Bits per entry in Red, Green and Blue Palette Color LUT Descriptor are not equal"); + } + else + { + DCMIOD_WARN("Bits per entry in Red, Green and Blue Palette Color LUT Descriptor are not equal"); + } + return OFFalse; + } + return OFTrue; + } + } + } + } + if (isError) + { + DCMIOD_ERROR("Could not read first value mapped in Red, Green or Blue Palette Color LUT Descriptor"); + } + else + { + DCMIOD_WARN("Could not read first value mapped in Red, Green or Blue Palette Color LUT Descriptor"); + } + return OFFalse; +} + + +OFBool IODPaletteColorLUTModule::checkDataConsistency(const OFBool& isError) +{ + // Check whether all 2nd values ("first value mapped") have same value. + Uint16 redFirstMapped, greenFirstMapped, blueFirstMapped, redNumEntries, greenNumEntries, blueNumEntries; + unsigned long redActualNumEntries, greenActualNumEntries, blueActualNumEntries; + redFirstMapped = greenFirstMapped = blueFirstMapped = redNumEntries = greenNumEntries = blueNumEntries = 0; + redActualNumEntries = greenActualNumEntries = blueActualNumEntries = 0; + const Uint8 bits = numBits(); + OFCondition result; + OFString message; + result = getRedPaletteColorLookupTableDescriptor(redFirstMapped, 1); + if (result.good()) + { + result = getGreenPaletteColorLookupTableDescriptor(greenFirstMapped, 1); + } + if (result.good()) + { + result = getBluePaletteColorLookupTableDescriptor(blueFirstMapped, 1); + } + if (result.bad()) + { + message = "Could not read first value mapped in Red, Green or Blue Palette Color LUT Descriptor"; + } + + if (result.good()) + { + if (getRedPaletteColorLookupTableDescriptor(redNumEntries, 0).good()) + { + if (getGreenPaletteColorLookupTableDescriptor(greenNumEntries, 0).good()) + { + if (getBluePaletteColorLookupTableDescriptor(blueNumEntries, 0).good()) + { + unsigned long maxEntries = 0; + if (bits == 8) + { + maxEntries = 256; + const Uint8* data = NULL; + result = getRedPaletteColorLookupTableData(data, redActualNumEntries); + delete[] data; + if (result.good()) + { + result = getGreenPaletteColorLookupTableData(data, greenActualNumEntries); + delete[] data; + } + if (result.good()) + { + result = getBluePaletteColorLookupTableData(data, blueActualNumEntries); + delete[] data; + } + } + else if (bits == 16) + { + maxEntries = 65536; + const Uint16* data = NULL; + result = getRedPaletteColorLookupTableData(data, redActualNumEntries); + delete[] data; + if (result.good()) + { + result = getGreenPaletteColorLookupTableData(data, greenActualNumEntries); + delete[] data; + } + if (result.good()) + { + result = getBluePaletteColorLookupTableData(data, blueActualNumEntries); + delete[] data; + } + } + if (result.good()) + { + if ( (redNumEntries != redActualNumEntries) || (greenNumEntries != greenActualNumEntries) || (blueNumEntries != blueActualNumEntries) ) + { + // also check special case where number descriptor entries is 0 (i.e. max value) + if ( (!redNumEntries && (redActualNumEntries != maxEntries)) || (!greenNumEntries && (greenActualNumEntries != maxEntries)) || (!blueNumEntries && (blueActualNumEntries != maxEntries)) ) + { + message = "Number of entries in Red, Green and Blue Palette Color LUT Data does not match descriptor"; + } + } + return OFTrue; + } + } + } + } + } + if (isError) + { + DCMIOD_ERROR(message); + } + else + { + DCMIOD_WARN(message); + } + return OFFalse; +} + + +OFBool IODPaletteColorLUTModule::checkSegmentConsistency(const OFBool& isError, OFBool& isSegmented) +{ + // Check that unsegmented LUTs are used together with segmented LUTs + isSegmented = OFFalse; + OFBool hasNonSegmentedLUTs = OFFalse; + OFString msg; + // Check for segmented LUT descriptors + if (m_Item->tagExists(DCM_SegmentedRedPaletteColorLookupTableData) && m_Item->tagExists(DCM_SegmentedGreenPaletteColorLookupTableData) && m_Item->tagExists(DCM_SegmentedBluePaletteColorLookupTableData)) + { + // Also check for segmented LUT data + if (m_Item->tagExists(DCM_SegmentedRedPaletteColorLookupTableData) && m_Item->tagExists(DCM_SegmentedGreenPaletteColorLookupTableData) && m_Item->tagExists(DCM_SegmentedBluePaletteColorLookupTableData)) + { + isSegmented = OFTrue; + } + } + // Check for unsegmented LUT descriptors + if (m_Item->tagExists(DCM_RedPaletteColorLookupTableDescriptor) || m_Item->tagExists(DCM_GreenPaletteColorLookupTableDescriptor) || m_Item->tagExists(DCM_BluePaletteColorLookupTableDescriptor)) + { + // Check for unsegmented LUT data + if (m_Item->tagExists(DCM_RedPaletteColorLookupTableData) || m_Item->tagExists(DCM_GreenPaletteColorLookupTableData) || m_Item->tagExists(DCM_BluePaletteColorLookupTableData)) + { + hasNonSegmentedLUTs = OFTrue; + } + } + // Check that both are not used together + if (isSegmented && hasNonSegmentedLUTs) + { + msg = "Segmented Palette LUT attributes are used together with Unsegmented LUT attributes"; + } + if (!isSegmented && !hasNonSegmentedLUTs) + { + msg = "No or incomplete Palette LUT attributes found"; + } + if (msg.length() > 0) + { + if (isError) + { + DCMIOD_ERROR(msg); + } + else + { + DCMIOD_WARN(msg); + } + return OFFalse; + } + return OFTrue; +} + + +OFCondition IODPaletteColorLUTModule::getUint8DataCopy(const DcmTagKey& dataTag, const Uint8*& lutData, unsigned long& num8BitEntries) +{ + OFCondition result = EC_Normal; + num8BitEntries = 0; + + const Uint16* data = NULL; + unsigned long num16BitEntries = 0; + // check first whether we actually have 8 bit data by checking third descriptor value + if (numBits() != 8) + { + DCMIOD_ERROR("Cannot convert 16 bit data to 8 bit data: Descriptor does not indicate 8 bit data"); + return EC_IllegalParameter; + } + result = m_Item->findAndGetUint16Array(dataTag, data, &num16BitEntries); + if (result.good()) + { + Uint16 numDescriptor = 0; + // get number of entries according to first value of descriptor + result = numEntriesForData(dataTag, numDescriptor); + if (result.bad()) + { + DCMIOD_ERROR("Could not determine number of 8 bit entries for " << dataTag); + return IOD_EC_InvalidColorPalette; + } + num8BitEntries = numDescriptor; + if (num8BitEntries == 0) + { + num8BitEntries = 256; + } + // check whether number of entries is consistent with number of 16 bit entries + if ( (num8BitEntries != num16BitEntries*2) && (num8BitEntries != num16BitEntries*2 -1) ) + { + DCMIOD_DEBUG("Number of 8 bit entries from descriptor: " << numDescriptor << ", Number of 16 bit entries in data: " << num16BitEntries); + DCMIOD_ERROR("Number of 8 bit entries does not match number of 16 bit entries"); + return IOD_EC_InvalidColorPalette; + } + Uint8* newData = new Uint8[num8BitEntries]; + if (num8BitEntries > 1) + { + for (unsigned long i = 0; i < num16BitEntries - 1; i++) + { + // extract lower and higher byte of 16 bit entry + newData[i * 2] = OFstatic_cast(Uint8, data[i] >> 8); + newData[i * 2 + 1] = OFstatic_cast(Uint8, data[i] & 0x00FF); + } + } + if (num8BitEntries % 2) + { + // if odd number of 16 bit entries, only extract lower byte of last entry + newData[num8BitEntries-1] = OFstatic_cast(Uint8, data[num16BitEntries-1] >> 8); + } + else + { + newData[num8BitEntries-2] = OFstatic_cast(Uint8, data[num16BitEntries-1] >> 8); + newData[num8BitEntries-1] = OFstatic_cast(Uint8, data[num16BitEntries-1] & 0x00FF); + } + lutData = newData; + } + return result; +} + + +OFCondition IODPaletteColorLUTModule::getUint16Data(const DcmTagKey& dataTag, const Uint16*& lutData, unsigned long& numEntries) +{ + OFCondition result; + const Uint16* data = NULL; + + result = m_Item->findAndGetUint16Array(dataTag, data, &numEntries, OFFalse); + if (result.good()) + { + lutData = data; + } + return result; +} + +OFCondition IODPaletteColorLUTModule::getUint16DataCopy(const DcmTagKey& dataTag, const Uint16*& lutData, unsigned long& numEntries) +{ + const Uint16* buffer; + OFCondition result = getUint16Data(dataTag, buffer, numEntries); + if (result.good()) + { + Uint16* newData = new Uint16[numEntries]; + // copy data + for (unsigned long i = 0; i < numEntries; i++) + { + newData[i] = buffer[i]; + } + lutData = newData; + } + return result; +} + + +OFCondition IODPaletteColorLUTModule::putUint8Data(const DcmTagKey& dataTag, const Uint8* lutData, const unsigned long num8BitEntries) +{ + if (numBits() != 8) + { + DCMIOD_ERROR("Cannot convert 8 bit data to 16 bit data: Descriptor does not indicate 8 bit data"); + return EC_IllegalParameter; + } + OFCondition result; + unsigned long num16BitEntries = num8BitEntries / 2 + (num8BitEntries % 2); + // put 16 bit data together by combining two 8 bit entries into one 16 bit entry + Uint16* newData = new Uint16[num16BitEntries]; + for (unsigned long i = 0; i < num16BitEntries; i++) + { + if (i != num16BitEntries-1) + { + newData[i] = OFstatic_cast(Uint16, lutData[i*2] << 8) | lutData[i*2+1]; + } + else + { + // if odd number of 8 bit entries, only use lower byte of last entry + newData[i] = OFstatic_cast(Uint16, lutData[i*2] << 8); + } + } + // Reset last entry (second part of 16 bit word) to 0 if odd number of 8 bit entries + if (num8BitEntries % 2) + { + newData[num16BitEntries-1] &= 0xFF00; + } + // otherwise also take over last 8 bit entry + else + { + newData[num16BitEntries-1] |= lutData[num8BitEntries-1]; + } + result = m_Item->putAndInsertUint16Array(dataTag, newData, num16BitEntries); + delete[] newData; + return result; +} + + +OFCondition IODPaletteColorLUTModule::numEntriesForData(const DcmTagKey& dataTag, Uint16& result) +{ + result = 0; + OFCondition cond; + if (dataTag == DCM_RedPaletteColorLookupTableData) + { + cond = getRedPaletteColorLookupTableDescriptor(result, 0); + } + else if (dataTag == DCM_GreenPaletteColorLookupTableData) + { + cond = getGreenPaletteColorLookupTableDescriptor(result, 0); + } + else if (dataTag == DCM_BluePaletteColorLookupTableData) + { + cond = getBluePaletteColorLookupTableDescriptor(result, 0); + } + else if (dataTag == DCM_SegmentedRedPaletteColorLookupTableData) + { + cond = getRedPaletteColorLookupTableDescriptor(result, 0); + } + else if (dataTag == DCM_SegmentedGreenPaletteColorLookupTableData) + { + cond = getGreenPaletteColorLookupTableDescriptor(result, 0); + } + else if (dataTag == DCM_SegmentedBluePaletteColorLookupTableData) + { + cond = getBluePaletteColorLookupTableDescriptor(result, 0); + } + else + { + return EC_InvalidTag; + } + return cond; +} diff --git a/dcmiod/tests/CMakeLists.txt b/dcmiod/tests/CMakeLists.txt index cdba8600..43265f41 100644 --- a/dcmiod/tests/CMakeLists.txt +++ b/dcmiod/tests/CMakeLists.txt @@ -3,12 +3,15 @@ DCMTK_ADD_TEST_EXECUTABLE(dcmiod_tests tchecks.cc tcielabutil.cc tcodes.cc + ticcprofile.cc timagepixel.cc + tmacro.cc + tpalette.cc tests.cc ) # make sure executables are linked to the corresponding libraries -DCMTK_TARGET_LINK_MODULES(dcmiod_tests dcmiod dcmdata oflog ofstd) +DCMTK_TARGET_LINK_MODULES(dcmiod_tests dcmimgle dcmiod dcmdata oflog ofstd) # This macro parses tests.cc and registers all tests DCMTK_ADD_TESTS(dcmiod) diff --git a/dcmiod/tests/Makefile.dep b/dcmiod/tests/Makefile.dep index eb860778..f69dab64 100644 --- a/dcmiod/tests/Makefile.dep +++ b/dcmiod/tests/Makefile.dep @@ -57,6 +57,7 @@ tchecks.o: tchecks.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ ../include/dcmtk/dcmiod/iodtypes.h ../include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../include/dcmtk/dcmiod/modcommoninstanceref.h \ ../include/dcmtk/dcmiod/iodmacro.h \ @@ -221,7 +222,9 @@ tcodes.o: tcodes.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ ../include/dcmtk/dcmiod/iodrules.h ../include/dcmtk/dcmiod/iodtypes.h \ - ../include/dcmtk/dcmiod/ioddef.h ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../include/dcmtk/dcmiod/modbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ @@ -278,6 +281,82 @@ tests.o: tests.cc ../../config/include/dcmtk/config/osconfig.h \ ../../oflog/include/dcmtk/oflog/logmacro.h \ ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ ../../oflog/include/dcmtk/oflog/tracelog.h +ticcprofile.o: ticcprofile.cc \ + ../../config/include/dcmtk/config/osconfig.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdict.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../dcmdata/include/dcmtk/dcmdata/dchashdi.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../include/dcmtk/dcmiod/modiccprofile.h \ + ../include/dcmtk/dcmiod/iodrules.h ../include/dcmtk/dcmiod/iodtypes.h \ + ../include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../include/dcmtk/dcmiod/modbase.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ + ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ + ../../ofstd/include/dcmtk/ofstd/oftest.h \ + ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ + ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ + ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ + ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ + ../../ofstd/include/dcmtk/ofstd/ofexit.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcuid.h timagepixel.o: timagepixel.cc \ ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmiod/iodimage.h ../include/dcmtk/dcmiod/iodcommn.h \ @@ -323,6 +402,8 @@ timagepixel.o: timagepixel.cc \ ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../include/dcmtk/dcmiod/modcommoninstanceref.h \ @@ -336,7 +417,6 @@ timagepixel.o: timagepixel.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ @@ -376,3 +456,159 @@ timagepixel.o: timagepixel.cc \ ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ ../../ofstd/include/dcmtk/ofstd/ofexit.h \ ../../dcmdata/include/dcmtk/dcmdata/dcuid.h +tmacro.o: tmacro.cc ../../config/include/dcmtk/config/osconfig.h \ + ../../ofstd/include/dcmtk/ofstd/oftest.h \ + ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ + ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../ofstd/include/dcmtk/ofstd/ofexit.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcuid.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../include/dcmtk/dcmiod/iodmacro.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrlo.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ + ../include/dcmtk/dcmiod/iodrules.h ../include/dcmtk/dcmiod/iodtypes.h \ + ../include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../include/dcmtk/dcmiod/modbase.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h +tpalette.o: tpalette.cc ../../config/include/dcmtk/config/osconfig.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdict.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../dcmdata/include/dcmtk/dcmdata/dchashdi.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../include/dcmtk/dcmiod/modpalettecolorlut.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../include/dcmtk/dcmiod/iodrules.h ../include/dcmtk/dcmiod/iodtypes.h \ + ../include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../include/dcmtk/dcmiod/modbase.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ + ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ + ../../ofstd/include/dcmtk/ofstd/oftest.h \ + ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ + ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ + ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ + ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ + ../../ofstd/include/dcmtk/ofstd/ofexit.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcuid.h diff --git a/dcmiod/tests/Makefile.in b/dcmiod/tests/Makefile.in index e3bf28e4..67059a32 100644 --- a/dcmiod/tests/Makefile.in +++ b/dcmiod/tests/Makefile.in @@ -17,15 +17,19 @@ ofstddir = $(top_srcdir)/../ofstd oflogdir = $(top_srcdir)/../oflog dcmdatadir = $(top_srcdir)/../dcmdata dcmioddir = $(top_srcdir)/../dcmiod +dcmimgledir = $(top_srcdir)/../dcmimgle LOCALINCLUDES = -I$(dcmioddir)/include -I$(dcmdatadir)/include -I$(oflogdir)/include \ -I$(ofstddir)/include LIBDIRS = -L$(top_srcdir)/libsrc -L$(dcmioddir)/libsrc -L$(dcmdatadir)/libsrc \ - -L$(oflogdir)/libsrc -L$(ofstddir)/libsrc -L$(oficonvdir)/libsrc -LOCALLIBS = -ldcmiod -ldcmdata -loflog -lofstd -loficonv \ + -L$(oflogdir)/libsrc -L$(ofstddir)/libsrc -L$(oficonvdir)/libsrc \ + -L$(dcmimgledir)/libsrc +LOCALLIBS = -ldcmiod -ldcmimgle -ldcmdata -loflog -lofstd -loficonv \ $(TIFFLIBS) $(PNGLIBS) $(ZLIBLIBS) $(CHARCONVLIBS) $(MATHLIBS) -test_objs = tests.o tchecks.o tcielabutil.o tcodes.o timagepixel.o +test_objs = tests.o tchecks.o tcielabutil.o tcodes.o ticcprofile.o \ + timagepixel.o tmacro.o tpalette.o + objs = tests.o $(test_objs) progs = tests diff --git a/dcmiod/tests/tchecks.cc b/dcmiod/tests/tchecks.cc index 544883ac..6a88c602 100644 --- a/dcmiod/tests/tchecks.cc +++ b/dcmiod/tests/tchecks.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2024, OFFIS e.V. + * Copyright (C) 2024-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -11,7 +11,7 @@ * D-26121 Oldenburg, Germany * * - * Module: dcmect + * Module: dcmiod * * Author: Michael Onken * diff --git a/dcmiod/tests/tests.cc b/dcmiod/tests/tests.cc index 83c00841..230fbcb1 100644 --- a/dcmiod/tests/tests.cc +++ b/dcmiod/tests/tests.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2016-2024, OFFIS e.V. + * Copyright (C) 2016-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -28,6 +28,11 @@ OFTEST_REGISTER(dcmiod_component_check_missing_value); OFTEST_REGISTER(dcmiod_component_check_vm_violated); OFTEST_REGISTER(dcmiod_component_check_vr_violated); OFTEST_REGISTER(dcmiod_codes); +OFTEST_REGISTER(dcmiod_content_identification_macro); +OFTEST_REGISTER(dcmiod_icc_profile_module); OFTEST_REGISTER(dcmiod_imagepixel); OFTEST_REGISTER(dcmiod_tcielabutil); +OFTEST_REGISTER(dcmiod_palette_color_lut_module); +OFTEST_REGISTER(dcmiod_palette_color_lut_module_segmented); +OFTEST_REGISTER(dcmiod_palette_color_lut_module_extra_checks); OFTEST_MAIN("dcmiod") diff --git a/dcmiod/tests/ticcprofile.cc b/dcmiod/tests/ticcprofile.cc new file mode 100644 index 00000000..faebfd49 --- /dev/null +++ b/dcmiod/tests/ticcprofile.cc @@ -0,0 +1,117 @@ +/* + * + * Copyright (C) 2024-2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmiod + * + * Author: Michael Onken + * + * Purpose: Tests for dcmiod's checks + * + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/dcmdata/dcdeftag.h" +#include "dcmtk/oflog/oflog.h" +#include "dcmtk/dcmdata/dcdict.h" +#include "dcmtk/dcmiod/modiccprofile.h" +#include "dcmtk/ofstd/oftest.h" + + +static const Uint8 ICC_LENGTH = 100; +static const OFString COLOR_SPACE("SRGB"); + +static void fillICCProfile(IODICCProfileModule& mod); +static void checkICCProfile(IODICCProfileModule& mod); +static void checkReadWriteICCProfile(IODICCProfileModule& mod); + +static OFLogger tLog = OFLog::getLogger("dcmtk.test.ticcprofile"); + + +/** Make sure dictionary is loaded + * @return OFTrue if dictionary is loaded, OFFalse otherwise + */ +static OFBool checkDictionary() +{ + // Make sure data dictionary is loaded + if (!dcmDataDict.isDictionaryLoaded()) + { + OFCHECK_FAIL("no data dictionary loaded, check environment variable: " DCM_DICT_ENVIRONMENT_VARIABLE); + return OFFalse; + } + return OFTrue; +} + +OFTEST(dcmiod_icc_profile_module) +{ + OFCHECK(checkDictionary()); + IODICCProfileModule mod; + + fillICCProfile(mod); + checkICCProfile(mod); + checkReadWriteICCProfile(mod); +} + + +static void fillICCProfile(IODICCProfileModule& mod) +{ + Uint8* iccProfile = new Uint8[ICC_LENGTH]; + for (Uint8 i = 0; i < ICC_LENGTH; i++) + { + iccProfile[i] = i; + } + OFCHECK(mod.setICCProfile(iccProfile, ICC_LENGTH, OFTrue /* check */).good()); + OFCHECK(mod.setColorSpace(COLOR_SPACE, OFTrue /* check */).good()); + + delete[] iccProfile; +} + + +static void checkICCProfile(IODICCProfileModule& mod) +{ + const Uint8* iccProfile = NULL; + Uint32 length = 0; + OFCHECK(mod.getICCProfile(iccProfile, length).good()); + OFCHECK(length == ICC_LENGTH); + for (unsigned long i = 0; i < ICC_LENGTH; i++) + { + OFCHECK(iccProfile[i] == i); + } + OFString colorSpace; + OFCHECK(mod.getColorSpace(colorSpace).good()); + OFCHECK(colorSpace == COLOR_SPACE); + delete [] iccProfile; // clean up, this is a copy +} + + +static void checkReadWriteICCProfile(IODICCProfileModule& mod) +{ + // Write to item and check all values using dcmdata API + DcmItem item; + OFCHECK(mod.write(item).good()); + const Uint8* profile = NULL; + unsigned long count = 0;; + OFCHECK(item.findAndGetUint8Array(DCM_ICCProfile, profile, &count).good()); + OFCHECK(count == ICC_LENGTH); + for (unsigned long i = 0; i < ICC_LENGTH; i++) + { + OFCHECK(profile[i] == i); + } + OFString colorSpace; + OFCHECK(item.findAndGetOFString(DCM_ColorSpace, colorSpace).good()); + OFCHECK(colorSpace == COLOR_SPACE); + + // Read from item and check all values from module class + IODICCProfileModule mod2; + OFCHECK(mod2.read(item).good()); + checkICCProfile(mod2); +} \ No newline at end of file diff --git a/dcmiod/tests/tmacro.cc b/dcmiod/tests/tmacro.cc new file mode 100644 index 00000000..b4ffa977 --- /dev/null +++ b/dcmiod/tests/tmacro.cc @@ -0,0 +1,66 @@ +/* + * + * Copyright (C) 2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmiod + * + * Author: Michael Onken + * + * Purpose: Tests for dcmiod macros + * + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/ofstd/oftest.h" +#include "dcmtk/dcmiod/iodmacro.h" + + +OFTEST(dcmiod_content_identification_macro) +{ + ContentIdentificationMacro macro; + OFCHECK((macro.check(OFTrue /* quiet */).bad())); + + OFCHECK(macro.setContentCreatorName("OC").good()); + OFCHECK(macro.setContentDescription("Test Description").good()); + OFCHECK(macro.setContentLabel("TEST_LABEL").good()); + OFCHECK(macro.setInstanceNumber("42").good()); + + OFCHECK(macro.check(OFTrue /* quiet */).good()); + OFString str; + + // Check all strings + OFCHECK(macro.getContentCreatorName(str).good()); + OFCHECK(str == "OC"); + OFCHECK(macro.getContentDescription(str).good()); + OFCHECK(str == "Test Description"); + OFCHECK(macro.getContentLabel(str).good()); + OFCHECK(str == "TEST_LABEL"); + OFCHECK(macro.getInstanceNumber(str).good()); + OFCHECK(str == "42"); + + // Write and re-read + DcmItem item; + OFCHECK(macro.write(item).good()); + ContentIdentificationMacro macro2; + OFCHECK(macro2.read(item).good()); + OFCHECK(macro2.check(OFTrue /* quiet */).good()); + + // Check strings in macro2 + OFCHECK(macro2.getContentCreatorName(str).good()); + OFCHECK(str == "OC"); + OFCHECK(macro2.getContentDescription(str).good()); + OFCHECK(str == "Test Description"); + OFCHECK(macro2.getContentLabel(str).good()); + OFCHECK(str == "TEST_LABEL"); + OFCHECK(macro2.getInstanceNumber(str).good()); + OFCHECK(str == "42"); +} diff --git a/dcmiod/tests/tpalette.cc b/dcmiod/tests/tpalette.cc new file mode 100644 index 00000000..0a5f6111 --- /dev/null +++ b/dcmiod/tests/tpalette.cc @@ -0,0 +1,419 @@ +/* + * + * Copyright (C) 2024-2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmiod + * + * Author: Michael Onken + * + * Purpose: Tests for dcmiod's checks + * + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/oflog/oflog.h" +#include "dcmtk/dcmdata/dcdict.h" +#include "dcmtk/dcmiod/modpalettecolorlut.h" +#include "dcmtk/ofstd/oftest.h" +#include "dcmtk/ofstd/oflimits.h" +#include "dcmtk/ofstd/ofdiag.h" + + +template static T* makePixelData(unsigned long& num_entries); +template static OFBool verifyPixelData(T* dataFound, T* data, const unsigned long& num_entries); +template static void createNonSegmentedPaletteModule(IODPaletteColorLUTModule& mod, Uint8 bits, const Uint16 numEntries, const T*& data); +template static void checkNonSegmentedPaletteModule(IODPaletteColorLUTModule& mod, Uint8 bits, const Uint16 numEntries, const T*& data); + +template static void createSegmentedPaletteModule(IODPaletteColorLUTModule& mod, Uint8 bits, const Uint16 numEntries, const T*& data); +template static void checkSegmentedPaletteModule(IODPaletteColorLUTModule& mod, Uint8 bits, const Uint16 numEntries, const T*& data); + + + +static void clear(IODPaletteColorLUTModule& mod); + +static OFLogger tLog = OFLog::getLogger("dcmtk.test.tpalette"); + +/** Make sure dictionary is loaded + * @return OFTrue if dictionary is loaded, OFFalse otherwise + */ +static OFBool checkDictionary() +{ + // Make sure data dictionary is loaded + if (!dcmDataDict.isDictionaryLoaded()) + { + OFCHECK_FAIL("no data dictionary loaded, check environment variable: " DCM_DICT_ENVIRONMENT_VARIABLE); + return OFFalse; + } + return OFTrue; +} + +OFTEST(dcmiod_palette_color_lut_module) +{ + OFCHECK(checkDictionary()); + IODPaletteColorLUTModule mod; + + // 16 Bit data + unsigned long num_entries = 65535; + Uint8 num_bits = 16; + const Uint16* data16bit = makePixelData(num_entries); + createNonSegmentedPaletteModule(mod, num_bits, OFstatic_cast(Uint16, num_entries), data16bit); + checkNonSegmentedPaletteModule(mod, num_bits, OFstatic_cast(Uint16, num_entries), data16bit); + clear(mod); + delete[] data16bit; + + // 16 bit max entries + num_entries = 65536; + num_bits = 16; + const Uint16* data16bitMax = makePixelData(num_entries); + createNonSegmentedPaletteModule(mod, num_bits, 0, data16bitMax); + checkNonSegmentedPaletteModule(mod, num_bits, 0, data16bitMax); + clear(mod); + delete[] data16bitMax; + + // 8 bit data + num_entries = 255; + num_bits = 8; + const Uint8* data8bit = makePixelData(num_entries); + createNonSegmentedPaletteModule(mod, num_bits, OFstatic_cast(Uint16, num_entries), data8bit); + checkNonSegmentedPaletteModule(mod, num_bits, OFstatic_cast(Uint16, num_entries), data8bit); + clear(mod); + delete[] data8bit; + + // 8 bit max entries + num_entries = 256; + num_bits = 8; + const Uint8* data8bitMax = makePixelData(num_entries); + createNonSegmentedPaletteModule(mod, num_bits, 0, data8bitMax); + checkNonSegmentedPaletteModule(mod, num_bits, 0, data8bitMax); + clear(mod); + delete[] data8bitMax; +} + +OFTEST(dcmiod_palette_color_lut_module_segmented) +{ + OFCHECK(checkDictionary()); + IODPaletteColorLUTModule mod; + + // 16 Bit data + unsigned long num_entries = 65535; + Uint8 num_bits = 16; + const Uint16* data16bit = makePixelData(num_entries); + createSegmentedPaletteModule(mod, num_bits, OFstatic_cast(Uint16, num_entries), data16bit); + checkSegmentedPaletteModule(mod, num_bits, OFstatic_cast(Uint16, num_entries), data16bit); + clear(mod); + delete[] data16bit; + + // 16 bit max entries + num_entries = 65536; + num_bits = 16; + const Uint16* data16bitMax = makePixelData(num_entries); + createSegmentedPaletteModule(mod, num_bits, 0, data16bitMax); + checkSegmentedPaletteModule(mod, num_bits, 0, data16bitMax); + clear(mod); + delete[] data16bitMax; + + // 8 bit data + num_entries = 255; + num_bits = 8; + const Uint8* data8bit = makePixelData(num_entries); + createSegmentedPaletteModule(mod, num_bits, OFstatic_cast(Uint16, num_entries), data8bit); + checkSegmentedPaletteModule(mod, num_bits, OFstatic_cast(Uint16, num_entries), data8bit); + clear(mod); + delete[] data8bit; + + // 8 bit max entries + num_entries = 256; + num_bits = 8; + const Uint8* data8bitMax = makePixelData(num_entries); + createSegmentedPaletteModule(mod, num_bits, 0, data8bitMax); + checkSegmentedPaletteModule(mod, num_bits, 0, data8bitMax); + clear(mod); + delete[] data8bitMax; +} + +OFTEST(dcmiod_palette_color_lut_module_extra_checks) +{ + // 16 bit data with 8 bit descriptor fails + IODPaletteColorLUTModule mod; + unsigned long num_entries = 255; + const Uint16* data16bit = makePixelData(num_entries); + OFCHECK(mod.setRedPaletteColorLookupTableDescriptor(OFstatic_cast(Uint16, num_entries), 0, 8).good()); + OFCHECK(mod.setGreenPaletteColorLookupTableDescriptor(OFstatic_cast(Uint16, num_entries), 0, 8).good()); + OFCHECK(mod.setBluePaletteColorLookupTableDescriptor(OFstatic_cast(Uint16, num_entries), 0, 8).good()); + // Now try to set 16 LUT Data + OFCHECK(mod.setRedPaletteColorLookupTableData(data16bit, num_entries).bad()); + OFCHECK(mod.setGreenPaletteColorLookupTableData(data16bit, num_entries).bad()); + OFCHECK(mod.setBluePaletteColorLookupTableData(data16bit, num_entries).bad()); + delete[] data16bit; + mod.clearData(); + + // 8 bit data with 16 bit descriptor fails + num_entries = 65535; + Uint8* data8bit = new Uint8[num_entries]; + memset(data8bit, 0, num_entries * sizeof(Uint8)); // vallues will be not exercised at all + OFCHECK(mod.setRedPaletteColorLookupTableDescriptor(OFstatic_cast(Uint16, num_entries), 0, 16).good()); + OFCHECK(mod.setGreenPaletteColorLookupTableDescriptor(OFstatic_cast(Uint16, num_entries), 0, 16).good()); + OFCHECK(mod.setBluePaletteColorLookupTableDescriptor(OFstatic_cast(Uint16, num_entries), 0, 16).good()); + // Now try to set 8 LUT Data + OFCHECK(mod.setRedPaletteColorLookupTableData(data8bit, num_entries).bad()); + OFCHECK(mod.setGreenPaletteColorLookupTableData(data8bit, num_entries).bad()); + OFCHECK(mod.setBluePaletteColorLookupTableData(data8bit, num_entries).bad()); + delete[] data8bit; + + // mix of segment and non segment data fails + num_entries = 65535; + DcmItem item; + data16bit = makePixelData(num_entries); + OFCHECK(mod.setRedPaletteColorLookupTableDescriptor(OFstatic_cast(Uint16, num_entries), 0, 16).good()); + OFCHECK(mod.setGreenPaletteColorLookupTableDescriptor(OFstatic_cast(Uint16, num_entries), 0, 16).good()); + OFCHECK(mod.setBluePaletteColorLookupTableDescriptor(OFstatic_cast(Uint16, num_entries), 0, 16).good()); + OFCHECK(mod.setSegmentedRedPaletteColorLookupTableData(data16bit, num_entries).good()); + OFCHECK(mod.setSegmentedGreenPaletteColorLookupTableData(data16bit, num_entries).good()); + OFCHECK(mod.setSegmentedBluePaletteColorLookupTableData(data16bit, num_entries).good()); + // this does not make sense: + OFCHECK(mod.setRedPaletteColorLookupTableData(data16bit, num_entries).good()); + OFCHECK(mod.setGreenPaletteColorLookupTableData(data16bit, num_entries).good()); + OFCHECK(mod.setBluePaletteColorLookupTableData(data16bit, num_entries).good()); + OFCHECK(mod.write(item).bad()); + delete[] data16bit; + +} + + +template +static T* makePixelData(unsigned long& num_entries) +{ +#include DCMTK_DIAGNOSTIC_PUSH +#include DCMTK_DIAGNOSTIC_IGNORE_VISUAL_STUDIO_CONSTANT_EXPRESSION_WARNING + if (!OFnumeric_limits::is_signed && (OFnumeric_limits::max() < num_entries - 1)) + { + OFCHECK_MSG(OFFalse, "Internal error, cannot create pixel data for given type T, too many entries."); + } + else if (OFnumeric_limits::is_signed && (OFstatic_cast(unsigned long, OFnumeric_limits::max()) / 2 > num_entries - 1)) + { + OFCHECK_MSG(OFFalse, "Internal error, cannot create pixel data for given type T, too many entries."); + } +#include DCMTK_DIAGNOSTIC_POP + + T* data = new T[num_entries]; + for (unsigned long i = 0; i < num_entries; i++) + { + // Casts are safe since we check the limits above, + // and this way the compiler does not complain about narrowing conversions + if (OFnumeric_limits::is_signed) + data[i] = OFstatic_cast(T, i - num_entries / 2); + else + data[i] = OFstatic_cast(T, i); + } + return data; +} + +template +static OFBool verifyPixelData(T* dataFound, T* data, const unsigned long& num_entries) +{ + if (!data || !dataFound) + { + return OFFalse; + } + + for (unsigned int i = 0; i < num_entries; i++) + { + if (dataFound[i] != data[i]) + { + OFLOG_DEBUG(tLog, "Pixel data at index " << OFstatic_cast(Uint16, i) << " is " << OFstatic_cast(Uint16, dataFound[i]) << " but should be " << OFstatic_cast(Uint16, data[i])); + return OFFalse; + } + } + return OFTrue; +} + +static void clear(IODPaletteColorLUTModule& mod) +{ + mod.clearData(); + OFCHECK(mod.numBits() == 0); +} + + +template +static void createNonSegmentedPaletteModule(IODPaletteColorLUTModule& mod, Uint8 bits, const Uint16 numEntries, const T*& data) +{ + OFCHECK_MSG(mod.setRedPaletteColorLookupTableDescriptor(numEntries, 0, bits).good(), "Cannot set Red Palette Color Lookup Table Descriptor"); + OFCHECK_MSG(mod.setGreenPaletteColorLookupTableDescriptor(numEntries, 0, bits).good(), "Cannot set Green Palette Color Lookup Table Descriptor"); + OFCHECK_MSG(mod.setBluePaletteColorLookupTableDescriptor(numEntries, 0, bits).good(), "Cannot set Blue Palette Color Lookup Table Descriptor"); + OFCHECK_MSG(mod.setPaletteColorLookupTableUID("1.2.3.4").good(), "Cannot set Palette Color Lookup Table UID (1.2.3.4)"); + + OFCHECK_MSG(mod.setRedPaletteColorLookupTableData(data, numEntries).good(), "Cannot set Red Palette Color Lookup Table Data"); + OFCHECK_MSG(mod.setGreenPaletteColorLookupTableData(data, numEntries).good(), "Cannot set Green Palette Color Lookup Table Data"); + OFCHECK_MSG(mod.setBluePaletteColorLookupTableData(data, numEntries).good(), "Cannot set Blue Palette Color Lookup Table Data"); +} + +template +static void createSegmentedPaletteModule(IODPaletteColorLUTModule& mod, Uint8 bits, const Uint16 numEntries, const T*& data) +{ + OFCHECK_MSG(mod.setRedPaletteColorLookupTableDescriptor(numEntries, 0, bits).good(), "Cannot set Red Palette Color Lookup Table Descriptor"); + OFCHECK_MSG(mod.setGreenPaletteColorLookupTableDescriptor(numEntries, 0, bits).good(), "Cannot set Green Palette Color Lookup Table Descriptor"); + OFCHECK_MSG(mod.setBluePaletteColorLookupTableDescriptor(numEntries, 0, bits).good(), "Cannot set Blue Palette Color Lookup Table Descriptor"); + OFCHECK_MSG(mod.setPaletteColorLookupTableUID("1.2.3.4").good(), "Cannot set Palette Color Lookup Table UID (1.2.3.4)"); + + OFCHECK_MSG(mod.setSegmentedRedPaletteColorLookupTableData(data, numEntries).good(), "Cannot set Segmented Red Palette Color Lookup Table Data"); + OFCHECK_MSG(mod.setSegmentedGreenPaletteColorLookupTableData(data, numEntries).good(), "Cannot set Segmented Green Palette Color Lookup Table Data"); + OFCHECK_MSG(mod.setSegmentedBluePaletteColorLookupTableData(data, numEntries).good(), "Cannot set Segmented Blue Palette Color Lookup Table Data"); +} + + +template +static void checkNonSegmentedPaletteModule(IODPaletteColorLUTModule& mod, Uint8 bits, const Uint16 numEntries, const T*& data) +{ + DcmItem item; + OFString uid; + Uint16 d1,d2,d3; + d1 = d2 = d3 = 1; // not used in test data + OFCHECK_MSG(mod.write(item).good(), "Cannot write Palette Color Lookup Table Module to DcmItem"); + OFCHECK_MSG(mod.numBits() == bits, "getBits() returns wrong value for Palette Color Lookup Table Module"); + OFCHECK_MSG(mod.getPaletteColorLookupTableUID(uid).good(), "Cannot get Palette Color Lookup Table UID"); + OFCHECK(mod.getRedPaletteColorLookupTableDescriptor(d1, 0).good()); + OFCHECK(mod.getRedPaletteColorLookupTableDescriptor(d2, 1).good()); + OFCHECK(mod.getRedPaletteColorLookupTableDescriptor(d3, 2).good()); + OFCHECK(d1 == numEntries); + OFCHECK(d2 == 0); + OFCHECK(d3 == bits); + d1 = d2 = d3 = 1; // not used in test data + OFCHECK(mod.getGreenPaletteColorLookupTableDescriptor(d1, 0).good()); + OFCHECK(mod.getGreenPaletteColorLookupTableDescriptor(d2, 1).good()); + OFCHECK(mod.getGreenPaletteColorLookupTableDescriptor(d3, 2).good()); + OFCHECK(d1 == numEntries); + OFCHECK(d2 == 0); + OFCHECK(d3 == bits); + d1 = d2 = d3 = 1; // not used in test data + OFCHECK(mod.getBluePaletteColorLookupTableDescriptor(d1, 0).good()); + OFCHECK(mod.getBluePaletteColorLookupTableDescriptor(d2, 1).good()); + OFCHECK(mod.getBluePaletteColorLookupTableDescriptor(d3, 2).good()); + OFCHECK(d1 == numEntries); + OFCHECK(d2 == 0); + OFCHECK(d3 == bits); + + OFCHECK(uid == "1.2.3.4"); + // 16 bit data + if (bits == 16) + { + unsigned long entriesFound = 0; + const Uint16 *dataFound16 = NULL; + OFCHECK(mod.getRedPaletteColorLookupTableData(dataFound16, entriesFound).good()); + OFCHECK(entriesFound == ((numEntries == 0) ? OFstatic_cast(unsigned long, 65536) : numEntries)); + OFCHECK(verifyPixelData(dataFound16, (const Uint16*)data, numEntries)); + delete[] dataFound16; dataFound16 = NULL; + OFCHECK(mod.getGreenPaletteColorLookupTableData(dataFound16, entriesFound).good()); + OFCHECK(entriesFound == ((numEntries == 0) ? OFstatic_cast(unsigned long, 65536) : numEntries)); + OFCHECK(verifyPixelData(dataFound16, (const Uint16*)data, numEntries)); + delete[] dataFound16; dataFound16 = NULL; + OFCHECK(mod.getBluePaletteColorLookupTableData(dataFound16, entriesFound).good()); + OFCHECK(entriesFound == ((numEntries == 0) ? OFstatic_cast(unsigned long, 65536) : numEntries)); + OFCHECK(verifyPixelData(dataFound16, (const Uint16*)data, numEntries)); + delete[] dataFound16; + } + // 8 bit data + else if (bits == 8) + { + unsigned long entriesFound = 0; + const Uint8 *dataFound8 = NULL; + OFCHECK(mod.getRedPaletteColorLookupTableData(dataFound8, entriesFound).good()); + OFCHECK(entriesFound == ((numEntries == 0) ? OFstatic_cast(unsigned long, 256) : numEntries)); + OFCHECK(verifyPixelData(dataFound8, (const Uint8*)data, numEntries)); + delete[] dataFound8; dataFound8 = NULL; + OFCHECK(mod.getGreenPaletteColorLookupTableData(dataFound8, entriesFound).good()); + OFCHECK(entriesFound == ((numEntries == 0) ? OFstatic_cast(unsigned long, 256) : numEntries)); + OFCHECK(verifyPixelData(dataFound8, (const Uint8*)data, numEntries)); + delete[] dataFound8; dataFound8 = NULL; + OFCHECK(mod.getBluePaletteColorLookupTableData(dataFound8, entriesFound).good()); + OFCHECK(entriesFound == ((numEntries == 0) ? OFstatic_cast(unsigned long, 256) : numEntries)); + OFCHECK(verifyPixelData(dataFound8, (const Uint8*)data, numEntries)); + delete[] dataFound8; + } + else + { + OFCHECK_FAIL("Unsupported bit depth"); + } +} + + +template +static void checkSegmentedPaletteModule(IODPaletteColorLUTModule& mod, Uint8 bits, const Uint16 numEntries, const T*& data) +{ + DcmItem item; + OFString uid; + unsigned long entriesFound = 0; + Uint16 d1,d2,d3; + d1 = d2 = d3 = 1; // not used in test data + OFCHECK_MSG(mod.write(item).good(), "Cannot write (segmented) Palette Color Lookup Table Module to DcmItem"); + OFCHECK_MSG(mod.numBits() == bits, "getBits() returns wrong value for Palette Color Lookup Table Module"); + OFCHECK_MSG(mod.getPaletteColorLookupTableUID(uid).good(), "Cannot get Palette Color Lookup Table UID"); + OFCHECK(mod.getRedPaletteColorLookupTableDescriptor(d1, 0).good()); + OFCHECK(mod.getRedPaletteColorLookupTableDescriptor(d2, 1).good()); + OFCHECK(mod.getRedPaletteColorLookupTableDescriptor(d3, 2).good()); + OFCHECK(d1 == numEntries); + OFCHECK(d2 == 0); + OFCHECK(d3 == bits); + d1 = d2 = d3 = 1; // not used in test data + OFCHECK(mod.getGreenPaletteColorLookupTableDescriptor(d1, 0).good()); + OFCHECK(mod.getGreenPaletteColorLookupTableDescriptor(d2, 1).good()); + OFCHECK(mod.getGreenPaletteColorLookupTableDescriptor(d3, 2).good()); + OFCHECK(d1 == numEntries); + OFCHECK(d2 == 0); + OFCHECK(d3 == bits); + d1 = d2 = d3 = 1; // not used in test data + OFCHECK(mod.getBluePaletteColorLookupTableDescriptor(d1, 0).good()); + OFCHECK(mod.getBluePaletteColorLookupTableDescriptor(d2, 1).good()); + OFCHECK(mod.getBluePaletteColorLookupTableDescriptor(d3, 2).good()); + OFCHECK(d1 == numEntries); + OFCHECK(d2 == 0); + OFCHECK(d3 == bits); + + OFCHECK(uid == "1.2.3.4"); + // 16 bit data + if (bits == 16) + { + const Uint16 *dataFound16 = NULL; + OFCHECK(mod.getSegmentedRedPaletteColorLookupTableData(dataFound16, entriesFound).good()); + OFCHECK(entriesFound == ((numEntries == 0) ? OFstatic_cast(unsigned long, 65536) : numEntries)); + OFCHECK(verifyPixelData(dataFound16, (const Uint16*)data, numEntries)); + delete[] dataFound16; dataFound16 = NULL; + OFCHECK(mod.getSegmentedGreenPaletteColorLookupTableData(dataFound16, entriesFound).good()); + OFCHECK(entriesFound == ((numEntries == 0) ? OFstatic_cast(unsigned long, 65536) : numEntries)); + OFCHECK(verifyPixelData(dataFound16, (const Uint16*)data, numEntries)); + delete[] dataFound16; dataFound16 = NULL; + OFCHECK(mod.getSegmentedBluePaletteColorLookupTableData(dataFound16, entriesFound).good()); + OFCHECK(entriesFound == ((numEntries == 0) ? OFstatic_cast(unsigned long, 65536) : numEntries)); + OFCHECK(verifyPixelData(dataFound16, (const Uint16*)data, numEntries)); + delete[] dataFound16; + } + // 8 bit data + else if (bits == 8) + { + const Uint8 *dataFound8 = NULL; + OFCHECK(mod.getSegmentedRedPaletteColorLookupTableData(dataFound8, entriesFound).good()); + OFCHECK(entriesFound == ((numEntries == 0) ? OFstatic_cast(unsigned long, 256) : numEntries)); + OFCHECK(verifyPixelData(dataFound8, (const Uint8*)data, numEntries)); + delete[] dataFound8; dataFound8 = NULL; + OFCHECK(mod.getSegmentedGreenPaletteColorLookupTableData(dataFound8, entriesFound).good()); + OFCHECK(entriesFound == ((numEntries == 0) ? OFstatic_cast(unsigned long, 256) : numEntries)); + OFCHECK(verifyPixelData(dataFound8, (const Uint8*)data, numEntries)); + delete[] dataFound8; dataFound8 = NULL; + OFCHECK(mod.getSegmentedBluePaletteColorLookupTableData(dataFound8, entriesFound).good()); + OFCHECK(entriesFound == ((numEntries == 0) ? OFstatic_cast(unsigned long, 256) : numEntries)); + OFCHECK(verifyPixelData(dataFound8, (const Uint8*)data, numEntries)); + delete[] dataFound8; + } + else + { + OFCHECK_FAIL("Unsupported bit depth"); + } +} \ No newline at end of file diff --git a/dcmjpeg/apps/dcmdjpeg.cc b/dcmjpeg/apps/dcmdjpeg.cc index 0c963638..9388bd1f 100644 --- a/dcmjpeg/apps/dcmdjpeg.cc +++ b/dcmjpeg/apps/dcmdjpeg.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2022, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -68,6 +68,7 @@ int main(int argc, char *argv[]) OFBool opt_predictor6WorkaroundEnable = OFFalse; OFBool opt_cornellWorkaroundEnable = OFFalse; OFBool opt_forceSingleFragmentPerFrame = OFFalse; + OFBool opt_preserveBitsStored = OFFalse; OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Decode JPEG-compressed DICOM file", rcsid); OFCommandLine cmd; @@ -102,6 +103,10 @@ int main(int argc, char *argv[]) cmd.addOption("--color-by-pixel", "+px", "always store color-by-pixel"); cmd.addOption("--color-by-plane", "+pl", "always store color-by-plane"); + cmd.addSubGroup("bits stored:"); + cmd.addOption("--bits-stored-fix", "+bs", "correct inconsistent bits stored value (default)"); + cmd.addOption("--bits-stored-keep", "-bs", "preserve inconsistent bits stored value"); + cmd.addSubGroup("SOP Instance UID:"); cmd.addOption("--uid-default", "+ud", "keep same SOP Instance UID (default)"); cmd.addOption("--uid-always", "+ua", "always assign new UID"); @@ -169,6 +174,11 @@ int main(int argc, char *argv[]) if (cmd.findOption("--color-by-plane")) opt_planarconfig = EPC_colorByPlane; cmd.endOptionBlock(); + cmd.beginOptionBlock(); + if (cmd.findOption("--bits-stored-fix")) opt_preserveBitsStored = OFFalse; + if (cmd.findOption("--bits-stored-keep")) opt_preserveBitsStored = OFTrue; + cmd.beginOptionBlock(); + cmd.beginOptionBlock(); if (cmd.findOption("--conv-photometric")) opt_decompCSconversion = EDC_photometricInterpretation; if (cmd.findOption("--conv-lossy")) opt_decompCSconversion = EDC_lossyOnly; @@ -266,7 +276,8 @@ int main(int argc, char *argv[]) opt_planarconfig, opt_predictor6WorkaroundEnable, opt_cornellWorkaroundEnable, - opt_forceSingleFragmentPerFrame); + opt_forceSingleFragmentPerFrame, + opt_preserveBitsStored); /* make sure data dictionary is loaded */ if (!dcmDataDict.isDictionaryLoaded()) diff --git a/dcmjpeg/docs/dcmcjpeg.man b/dcmjpeg/docs/dcmcjpeg.man index 41079df7..2d7c44f6 100644 --- a/dcmjpeg/docs/dcmcjpeg.man +++ b/dcmjpeg/docs/dcmcjpeg.man @@ -707,6 +707,6 @@ It is an error if no data dictionary can be loaded. \section dcmcjpeg_copyright COPYRIGHT -Copyright (C) 2001-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2001-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmjpeg/docs/dcmdjpeg.man b/dcmjpeg/docs/dcmdjpeg.man index 52898187..45de63bd 100644 --- a/dcmjpeg/docs/dcmdjpeg.man +++ b/dcmjpeg/docs/dcmdjpeg.man @@ -147,6 +147,21 @@ planar configuration: # If the compressed image is a color image, store in color-by-plane # planar configuration. +bits stored: + + +bs --bits-stored-fix + correct inconsistent bits stored value (default) + + # If the value of BitsStored in the compressed bitstream is smaller + # than the value in the DICOM dataset, update the value in the dataset. + + -bs --bits-stored-keep + preserve inconsistent bits stored value + + # Keep the value of BitsStored even if inconsistent with the + # compressed bitstream. This may help in correctly decoding some + # defective images. + SOP Instance UID: +ud --uid-default @@ -347,6 +362,6 @@ It is an error if no data dictionary can be loaded. \section dcmdjpeg_copyright COPYRIGHT -Copyright (C) 2001-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2001-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmjpeg/docs/dcmj2pnm.man b/dcmjpeg/docs/dcmj2pnm.man index 8714ecd3..7a7d65c2 100644 --- a/dcmjpeg/docs/dcmj2pnm.man +++ b/dcmjpeg/docs/dcmj2pnm.man @@ -23,6 +23,6 @@ the same command line parameters, and more. \section dcmj2pnm_copyright COPYRIGHT -Copyright (C) 2001-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2001-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmjpeg/docs/dcmmkdir.man b/dcmjpeg/docs/dcmmkdir.man index 7ffce8f1..eaa201f0 100644 --- a/dcmjpeg/docs/dcmmkdir.man +++ b/dcmjpeg/docs/dcmmkdir.man @@ -508,6 +508,6 @@ It is an error if no data dictionary can be loaded. \section dcmmkdir_copyright COPYRIGHT -Copyright (C) 2001-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2001-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmjpeg/include/dcmtk/dcmjpeg/djcodecd.h b/dcmjpeg/include/dcmtk/dcmjpeg/djcodecd.h index 7c31575d..0d029545 100644 --- a/dcmjpeg/include/dcmtk/dcmjpeg/djcodecd.h +++ b/dcmjpeg/include/dcmtk/dcmjpeg/djcodecd.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2020, OFFIS e.V. + * Copyright (C) 2001-2024, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -177,6 +177,19 @@ public: const E_TransferSyntax oldRepType, const E_TransferSyntax newRepType) const; + /** determines the effective value of BitsAllocated that a dataset will have + * after decompression of an image with the given values for bitsAllocated + * and bitsStored. This may differ from the bitsAllocated parameter for example + * if that value is not a multiple of 8. Returns zero if an image with the + * given parameters cannot be decoded with this codec. + * @param bitsAllocated current value of Bits Allocated + * @param bitsStored current value of Bits Stored + * @return value of BitsAllocated after decompression, 0 if no decompression possible + */ + virtual Uint16 decodedBitsAllocated( + Uint16 bitsAllocated, + Uint16 bitsStored) const; + /** determine color model of the decompressed image * @param fromParam representation parameter of current compressed * representation, may be NULL diff --git a/dcmjpeg/include/dcmtk/dcmjpeg/djcodece.h b/dcmjpeg/include/dcmtk/dcmjpeg/djcodece.h index 7f3b6bc4..553df6e3 100644 --- a/dcmjpeg/include/dcmtk/dcmjpeg/djcodece.h +++ b/dcmjpeg/include/dcmtk/dcmjpeg/djcodece.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2020, OFFIS e.V. + * Copyright (C) 2001-2024, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -181,6 +181,19 @@ public: const E_TransferSyntax oldRepType, const E_TransferSyntax newRepType) const; + /** determines the effective value of BitsAllocated that a dataset will have + * after decompression of an image with the given values for bitsAllocated + * and bitsStored. This may differ from the bitsAllocated parameter for example + * if that value is not a multiple of 8. Returns zero if an image with the + * given parameters cannot be decoded with this codec. + * @param bitsAllocated current value of Bits Allocated + * @param bitsStored current value of Bits Stored + * @return value of BitsAllocated after decompression, 0 if no decompression possible + */ + virtual Uint16 decodedBitsAllocated( + Uint16 bitsAllocated, + Uint16 bitsStored) const; + /** determine color model of the decompressed image * @param fromParam representation parameter of current compressed * representation, may be NULL diff --git a/dcmjpeg/include/dcmtk/dcmjpeg/djcparam.h b/dcmjpeg/include/dcmtk/dcmjpeg/djcparam.h index b8f234c2..7a620a84 100644 --- a/dcmjpeg/include/dcmtk/dcmjpeg/djcparam.h +++ b/dcmjpeg/include/dcmtk/dcmjpeg/djcparam.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1997-2018, OFFIS e.V. + * Copyright (C) 1997-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -68,6 +68,7 @@ public: * @param pAcrNemaCompatibility accept old ACR-NEMA images without photometric interpretation * (only "pseudo" lossless encoder) * @param pTrueLosslessMode Enables true lossless compression (replaces old "pseudo lossless" encoder) + * @param setPreserveBitsStored preserve BitsStored when decompressing even if inconsistent with J2K bitstream */ DJCodecParameter( E_CompressionColorSpaceConversion pCompressionCSConversion, @@ -97,7 +98,8 @@ public: OFBool pUseModalityRescale = OFFalse, OFBool pAcceptWrongPaletteTags = OFFalse, OFBool pAcrNemaCompatibility = OFFalse, - OFBool pTrueLosslessMode = OFTrue); + OFBool pTrueLosslessMode = OFTrue, + OFBool setPreserveBitsStored = OFFalse); /// copy constructor DJCodecParameter(const DJCodecParameter& arg); @@ -323,6 +325,15 @@ public: return forceSingleFragmentPerFrame; } + /** returns flag indicating whether to preserve the + * value of BitsStored even if inconsistent with the + * compressed bitstream. + */ + OFBool getPreserveBitsStored() const + { + return setPreserveBitsStored_; + } + private: /// private undefined copy assignment operator @@ -427,6 +438,9 @@ private: */ OFBool forceSingleFragmentPerFrame; + /// flag indicating whether BitsStored should be preserved when decompressing even if inconsistent with the J2K bitstream + OFBool setPreserveBitsStored_; + }; diff --git a/dcmjpeg/include/dcmtk/dcmjpeg/djdecode.h b/dcmjpeg/include/dcmtk/dcmjpeg/djdecode.h index d2d5c791..f6468c97 100644 --- a/dcmjpeg/include/dcmtk/dcmjpeg/djdecode.h +++ b/dcmjpeg/include/dcmtk/dcmjpeg/djdecode.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1997-2018, OFFIS e.V. + * Copyright (C) 1997-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -52,6 +52,7 @@ public: * Huffman table overflow * @param pForceSingleFragmentPerFrame while decompressing a multiframe image, * assume one fragment per frame even if the JPEG data for some frame is incomplete + * @param setPreserveBitsStored preserve BitsStored when decompressing even if inconsistent with J2K bitstream */ static void registerCodecs( E_DecompressionColorSpaceConversion pDecompressionCSConversion = EDC_photometricInterpretation, @@ -59,7 +60,8 @@ public: E_PlanarConfiguration pPlanarConfiguration = EPC_default, OFBool predictor6WorkaroundEnable = OFFalse, OFBool cornellWorkaroundEnable = OFFalse, - OFBool pForceSingleFragmentPerFrame = OFFalse); + OFBool pForceSingleFragmentPerFrame = OFFalse, + OFBool setPreserveBitsStored = OFFalse); /** deregisters decoders. * Attention: Must not be called while other threads might still use diff --git a/dcmjpeg/libijg12/jconfig12.h b/dcmjpeg/libijg12/jconfig12.h index a6fcb5dd..d50305cc 100644 --- a/dcmjpeg/libijg12/jconfig12.h +++ b/dcmjpeg/libijg12/jconfig12.h @@ -23,12 +23,12 @@ #include "dcmtk/config/osconfig.h" -/* We assume ANSI C and don't support DOS, - * so the following settings need not be tested +/* We assume ANSI C and don't support DOS, + * so the following settings need not be tested */ -#define HAVE_PROTOTYPES -#define HAVE_UNSIGNED_CHAR -#define HAVE_UNSIGNED_SHORT +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT #undef NEED_FAR_POINTERS #undef INCOMPLETE_TYPES_BROKEN @@ -38,9 +38,7 @@ #define CHAR_IS_UNSIGNED #endif -#ifdef HAVE_SYS_TYPES_H #define NEED_SYS_TYPES_H -#endif /* must always be defined for our implementation */ #define NEED_SHORT_EXTERNAL_NAMES diff --git a/dcmjpeg/libijg12/jerror.c b/dcmjpeg/libijg12/jerror.c index f564f09b..3cdeec1d 100644 --- a/dcmjpeg/libijg12/jerror.c +++ b/dcmjpeg/libijg12/jerror.c @@ -191,7 +191,6 @@ format_message (j_common_ptr cinfo, char * buffer) } /* Format the message into the passed buffer */ -#ifdef HAVE_VSNPRINTF if (isstring) snprintf(buffer, JMSG_LENGTH_MAX, msgtext, err->msg_parm.s); else @@ -200,16 +199,6 @@ format_message (j_common_ptr cinfo, char * buffer) err->msg_parm.i[2], err->msg_parm.i[3], err->msg_parm.i[4], err->msg_parm.i[5], err->msg_parm.i[6], err->msg_parm.i[7]); -#else /* HAVE_VSNPRINTF */ - if (isstring) - sprintf(buffer, msgtext, err->msg_parm.s); - else - sprintf(buffer, msgtext, - err->msg_parm.i[0], err->msg_parm.i[1], - err->msg_parm.i[2], err->msg_parm.i[3], - err->msg_parm.i[4], err->msg_parm.i[5], - err->msg_parm.i[6], err->msg_parm.i[7]); -#endif /* HAVE_VSNPRINTF */ } diff --git a/dcmjpeg/libijg16/jconfig16.h b/dcmjpeg/libijg16/jconfig16.h index a6fcb5dd..d50305cc 100644 --- a/dcmjpeg/libijg16/jconfig16.h +++ b/dcmjpeg/libijg16/jconfig16.h @@ -23,12 +23,12 @@ #include "dcmtk/config/osconfig.h" -/* We assume ANSI C and don't support DOS, - * so the following settings need not be tested +/* We assume ANSI C and don't support DOS, + * so the following settings need not be tested */ -#define HAVE_PROTOTYPES -#define HAVE_UNSIGNED_CHAR -#define HAVE_UNSIGNED_SHORT +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT #undef NEED_FAR_POINTERS #undef INCOMPLETE_TYPES_BROKEN @@ -38,9 +38,7 @@ #define CHAR_IS_UNSIGNED #endif -#ifdef HAVE_SYS_TYPES_H #define NEED_SYS_TYPES_H -#endif /* must always be defined for our implementation */ #define NEED_SHORT_EXTERNAL_NAMES diff --git a/dcmjpeg/libijg16/jerror.c b/dcmjpeg/libijg16/jerror.c index 1ad6a6e4..2310eae7 100644 --- a/dcmjpeg/libijg16/jerror.c +++ b/dcmjpeg/libijg16/jerror.c @@ -191,7 +191,6 @@ format_message (j_common_ptr cinfo, char * buffer) } /* Format the message into the passed buffer */ -#ifdef HAVE_VSNPRINTF if (isstring) snprintf(buffer, JMSG_LENGTH_MAX, msgtext, err->msg_parm.s); else @@ -200,16 +199,6 @@ format_message (j_common_ptr cinfo, char * buffer) err->msg_parm.i[2], err->msg_parm.i[3], err->msg_parm.i[4], err->msg_parm.i[5], err->msg_parm.i[6], err->msg_parm.i[7]); -#else /* HAVE_VSNPRINTF */ - if (isstring) - sprintf(buffer, msgtext, err->msg_parm.s); - else - sprintf(buffer, msgtext, - err->msg_parm.i[0], err->msg_parm.i[1], - err->msg_parm.i[2], err->msg_parm.i[3], - err->msg_parm.i[4], err->msg_parm.i[5], - err->msg_parm.i[6], err->msg_parm.i[7]); -#endif /* HAVE_VSNPRINTF */ } diff --git a/dcmjpeg/libijg8/jconfig8.h b/dcmjpeg/libijg8/jconfig8.h index a6fcb5dd..d50305cc 100644 --- a/dcmjpeg/libijg8/jconfig8.h +++ b/dcmjpeg/libijg8/jconfig8.h @@ -23,12 +23,12 @@ #include "dcmtk/config/osconfig.h" -/* We assume ANSI C and don't support DOS, - * so the following settings need not be tested +/* We assume ANSI C and don't support DOS, + * so the following settings need not be tested */ -#define HAVE_PROTOTYPES -#define HAVE_UNSIGNED_CHAR -#define HAVE_UNSIGNED_SHORT +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT #undef NEED_FAR_POINTERS #undef INCOMPLETE_TYPES_BROKEN @@ -38,9 +38,7 @@ #define CHAR_IS_UNSIGNED #endif -#ifdef HAVE_SYS_TYPES_H #define NEED_SYS_TYPES_H -#endif /* must always be defined for our implementation */ #define NEED_SHORT_EXTERNAL_NAMES diff --git a/dcmjpeg/libijg8/jerror.c b/dcmjpeg/libijg8/jerror.c index d87569de..3e873630 100644 --- a/dcmjpeg/libijg8/jerror.c +++ b/dcmjpeg/libijg8/jerror.c @@ -191,7 +191,6 @@ format_message (j_common_ptr cinfo, char * buffer) } /* Format the message into the passed buffer */ -#ifdef HAVE_VSNPRINTF if (isstring) snprintf(buffer, JMSG_LENGTH_MAX, msgtext, err->msg_parm.s); else @@ -200,16 +199,6 @@ format_message (j_common_ptr cinfo, char * buffer) err->msg_parm.i[2], err->msg_parm.i[3], err->msg_parm.i[4], err->msg_parm.i[5], err->msg_parm.i[6], err->msg_parm.i[7]); -#else /* HAVE_VSNPRINTF */ - if (isstring) - sprintf(buffer, msgtext, err->msg_parm.s); - else - sprintf(buffer, msgtext, - err->msg_parm.i[0], err->msg_parm.i[1], - err->msg_parm.i[2], err->msg_parm.i[3], - err->msg_parm.i[4], err->msg_parm.i[5], - err->msg_parm.i[6], err->msg_parm.i[7]); -#endif /* HAVE_VSNPRINTF */ } diff --git a/dcmjpeg/libsrc/Makefile.dep b/dcmjpeg/libsrc/Makefile.dep index 2b8c62f5..ea86854d 100644 --- a/dcmjpeg/libsrc/Makefile.dep +++ b/dcmjpeg/libsrc/Makefile.dep @@ -84,6 +84,7 @@ ddpiimpl.o: ddpiimpl.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dipixel.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimomod.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dibaslut.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimoopx.h \ ../../dcmimgle/include/dcmtk/dcmimgle/didispfn.h \ diff --git a/dcmjpeg/libsrc/djcodecd.cc b/dcmjpeg/libsrc/djcodecd.cc index 5b0eeea2..ce379e10 100644 --- a/dcmjpeg/libsrc/djcodecd.cc +++ b/dcmjpeg/libsrc/djcodecd.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2024, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -62,6 +62,24 @@ OFBool DJCodecDecoder::canChangeCoding( } +Uint16 DJCodecDecoder::decodedBitsAllocated( + Uint16 /* bitsAllocated */, + Uint16 bitsStored) const +{ + // this codec does not support images with less than 2 bits per sample + if (bitsStored < 2) return 0; + + // for images with 2..8 bits per sample, BitsAllocated will be 8 + if (bitsStored <= 8) return 8; + + // for images with 9..16 bits per sample, BitsAllocated will be 16 + if (bitsStored <= 16) return 16; + + // this codec does not support images with more than 16 bits per sample + return 0; +} + + OFCondition DJCodecDecoder::decode( const DcmRepresentationParameter * fromRepParam, DcmPixelSequence * pixSeq, @@ -328,13 +346,13 @@ OFCondition DJCodecDecoder::decode( } // Bits Stored cannot be larger than precision - if ((result.good()) && (imageBitsStored > precision)) + if ((result.good()) && (imageBitsStored > precision) && (! djcp->getPreserveBitsStored())) { result = OFreinterpret_cast(DcmItem*, dataset)->putAndInsertUint16(DCM_BitsStored, precision); } // High Bit cannot be larger than precision - 1 - if ((result.good()) && (imageHighBit >= precision)) + if ((result.good()) && (imageHighBit >= precision) && (! djcp->getPreserveBitsStored())) { result = OFreinterpret_cast(DcmItem*, dataset)->putAndInsertUint16(DCM_HighBit, OFstatic_cast(Uint16, precision-1)); } @@ -570,6 +588,11 @@ OFCondition DJCodecDecoder::decodeFrame( // decompression is complete, finally adjust byte order if necessary if (jpeg->bytesPerSample() == 1) // we're writing bytes into words { + if ((gLocalByteOrder == EBO_BigEndian) && (frameSize & 1)) + { + DCMJPEG_WARN("Size of frame buffer is odd, cannot correct byte order for last pixel value"); + } + result = swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, OFreinterpret_cast(Uint16*, buffer), OFstatic_cast(Uint32, frameSize), sizeof(Uint16)); } } diff --git a/dcmjpeg/libsrc/djcodece.cc b/dcmjpeg/libsrc/djcodece.cc index de973c5d..50ae1abb 100644 --- a/dcmjpeg/libsrc/djcodece.cc +++ b/dcmjpeg/libsrc/djcodece.cc @@ -75,6 +75,14 @@ OFBool DJCodecEncoder::canChangeCoding( } +Uint16 DJCodecEncoder::decodedBitsAllocated( + Uint16 /* bitsAllocated */, + Uint16 /* bitsStored */) const +{ + return 0; +} + + OFCondition DJCodecEncoder::decode( const DcmRepresentationParameter * /* fromRepParam */, DcmPixelSequence * /* pixSeq */, diff --git a/dcmjpeg/libsrc/djcparam.cc b/dcmjpeg/libsrc/djcparam.cc index 6c46eb5e..cf7f85a9 100644 --- a/dcmjpeg/libsrc/djcparam.cc +++ b/dcmjpeg/libsrc/djcparam.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1997-2018, OFFIS e.V. + * Copyright (C) 1997-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -50,7 +50,8 @@ DJCodecParameter::DJCodecParameter( OFBool pUseModalityRescale, OFBool pAcceptWrongPaletteTags, OFBool pAcrNemaCompatibility, - OFBool pTrueLosslessMode) + OFBool pTrueLosslessMode, + OFBool setPreserveBitsStored) : DcmCodecParameter() , compressionCSConversion(pCompressionCSConversion) , decompressionCSConversion(pDecompressionCSConversion) @@ -80,6 +81,7 @@ DJCodecParameter::DJCodecParameter( , predictor6WorkaroundEnabled_(predictor6WorkaroundEnable) , cornellWorkaroundEnabled_(cornellWorkaroundEnable) , forceSingleFragmentPerFrame(pForceSingleFragmentPerFrame) +, setPreserveBitsStored_(setPreserveBitsStored) { } @@ -114,6 +116,7 @@ DJCodecParameter::DJCodecParameter(const DJCodecParameter& arg) , predictor6WorkaroundEnabled_(arg.predictor6WorkaroundEnabled_) , cornellWorkaroundEnabled_(arg.cornellWorkaroundEnabled_) , forceSingleFragmentPerFrame(arg.forceSingleFragmentPerFrame) +, setPreserveBitsStored_(arg.setPreserveBitsStored_) { } diff --git a/dcmjpeg/libsrc/djdecode.cc b/dcmjpeg/libsrc/djdecode.cc index 9166c7bc..f27ccf68 100644 --- a/dcmjpeg/libsrc/djdecode.cc +++ b/dcmjpeg/libsrc/djdecode.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1997-2018, OFFIS e.V. + * Copyright (C) 1997-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -47,7 +47,8 @@ void DJDecoderRegistration::registerCodecs( E_PlanarConfiguration pPlanarConfiguration, OFBool predictor6WorkaroundEnable, OFBool cornellWorkaroundEnable, - OFBool pForceSingleFragmentPerFrame) + OFBool pForceSingleFragmentPerFrame, + OFBool setPreserveBitsStored) { if (! registered) { @@ -58,7 +59,12 @@ void DJDecoderRegistration::registerCodecs( pPlanarConfiguration, predictor6WorkaroundEnable, cornellWorkaroundEnable, - pForceSingleFragmentPerFrame); + pForceSingleFragmentPerFrame, + OFFalse, 0, 0, 0, OFTrue, + ESS_444, OFFalse, OFFalse, + 0, 0, 0.0, 0.0, 0, 0, 0, 0, + OFTrue, OFFalse, OFFalse, OFFalse, OFTrue, + setPreserveBitsStored); if (cp) { diff --git a/dcmjpls/apps/CMakeLists.txt b/dcmjpls/apps/CMakeLists.txt index f5591046..c8719f89 100644 --- a/dcmjpls/apps/CMakeLists.txt +++ b/dcmjpls/apps/CMakeLists.txt @@ -8,5 +8,5 @@ endforeach() # make sure executables are linked to the corresponding libraries foreach(PROGRAM dcmcjpls dcmdjpls dcml2pnm) - DCMTK_TARGET_LINK_MODULES(${PROGRAM} dcmjpls dcmtkcharls dcmimage dcmimgle dcmdata oflog ofstd ofstd) + DCMTK_TARGET_LINK_MODULES(${PROGRAM} dcmjpls dcmtkcharls dcmimage) endforeach() diff --git a/dcmjpls/docs/dcmcjpls.man b/dcmjpls/docs/dcmcjpls.man index d5d06560..83cb2512 100644 --- a/dcmjpls/docs/dcmcjpls.man +++ b/dcmjpls/docs/dcmcjpls.man @@ -387,6 +387,6 @@ It is an error if no data dictionary can be loaded. \section dcmcjpls_copyright COPYRIGHT -Copyright (C) 2009-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2009-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmjpls/docs/dcmdjpls.man b/dcmjpls/docs/dcmdjpls.man index 9e7eee3f..93c516fd 100644 --- a/dcmjpls/docs/dcmdjpls.man +++ b/dcmjpls/docs/dcmdjpls.man @@ -280,6 +280,6 @@ It is an error if no data dictionary can be loaded. \section dcmdjpls_copyright COPYRIGHT -Copyright (C) 2009-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2009-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmjpls/docs/dcml2pnm.man b/dcmjpls/docs/dcml2pnm.man index 86d162fc..4cd84451 100644 --- a/dcmjpls/docs/dcml2pnm.man +++ b/dcmjpls/docs/dcml2pnm.man @@ -23,6 +23,6 @@ the same command line parameters, and more. \section dcml2pnm_copyright COPYRIGHT -Copyright (C) 2001-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2001-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmjpls/include/dcmtk/dcmjpls/djcodecd.h b/dcmjpls/include/dcmtk/dcmjpls/djcodecd.h index 611d9b4d..099186b6 100644 --- a/dcmjpls/include/dcmtk/dcmjpls/djcodecd.h +++ b/dcmjpls/include/dcmtk/dcmjpls/djcodecd.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2007-2020, OFFIS e.V. + * Copyright (C) 2007-2024, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -170,6 +170,19 @@ public: const E_TransferSyntax oldRepType, const E_TransferSyntax newRepType) const; + /** determines the effective value of BitsAllocated that a dataset will have + * after decompression of an image with the given values for bitsAllocated + * and bitsStored. This may differ from the bitsAllocated parameter for example + * if that value is not a multiple of 8. Returns zero if an image with the + * given parameters cannot be decoded with this codec. + * @param bitsAllocated current value of Bits Allocated + * @param bitsStored current value of Bits Stored + * @return value of BitsAllocated after decompression, 0 if no decompression possible + */ + virtual Uint16 decodedBitsAllocated( + Uint16 bitsAllocated, + Uint16 bitsStored) const; + /** determine color model of the decompressed image * @param fromParam representation parameter of current compressed * representation, may be NULL @@ -239,6 +252,45 @@ private: Uint16 imageSamplesPerPixel, Uint16 bytesPerSample); + /** decompresses a single frame from the given pixel sequence and + * stores the result in the given buffer, without adjusting byte order + * @param fromPixSeq compressed pixel sequence + * @param cp codec parameters for this codec + * @param dataset pointer to dataset in which pixel data element is contained + * @param frameNo number of frame, starting with 0 for the first frame + * @param startFragment index of the compressed fragment that contains + * all or the first part of the compressed bitstream for the given frameNo. + * Upon successful return this parameter is updated to contain the index + * of the first compressed fragment of the next frame. + * When unknown, zero should be passed. In this case the decompression + * algorithm will try to determine the index by itself, which will always + * work if frames are decompressed in increasing order from first to last, + * but may fail if frames are decompressed in random order, multiple fragments + * per frame and multiple frames are present in the dataset, and the offset + * table is empty. + * @param buffer pointer to buffer where frame is to be stored + * @param bufSize size of buffer in bytes + * @param imageFrames number of frames in this image + * @param imageColumns number of columns for each frame + * @param imageRows number of rows for each frame + * @param imageSamplesPerPixel number of samples per pixel + * @param bytesPerSample number of bytes per sample + * @return EC_Normal if successful, an error code otherwise. + */ + static OFCondition decodeFrameNoSwap( + DcmPixelSequence * fromPixSeq, + const DJLSCodecParameter *cp, + DcmItem *dataset, + Uint32 frameNo, + Uint32& startFragment, + void *buffer, + Uint32 bufSize, + Sint32 imageFrames, + Uint16 imageColumns, + Uint16 imageRows, + Uint16 imageSamplesPerPixel, + Uint16 bytesPerSample); + /** determines if a given image requires color-by-plane planar configuration * depending on SOP Class UID (DICOM IOD) and photometric interpretation. * All SOP classes defined in the 2003 edition of the DICOM standard or earlier diff --git a/dcmjpls/include/dcmtk/dcmjpls/djcodece.h b/dcmjpls/include/dcmtk/dcmjpls/djcodece.h index 80019505..39c3e5d1 100644 --- a/dcmjpls/include/dcmtk/dcmjpls/djcodece.h +++ b/dcmjpls/include/dcmtk/dcmjpls/djcodece.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2007-2022, OFFIS e.V. + * Copyright (C) 2007-2024, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -173,6 +173,19 @@ public: const E_TransferSyntax oldRepType, const E_TransferSyntax newRepType) const; + /** determines the effective value of BitsAllocated that a dataset will have + * after decompression of an image with the given values for bitsAllocated + * and bitsStored. This may differ from the bitsAllocated parameter for example + * if that value is not a multiple of 8. Returns zero if an image with the + * given parameters cannot be decoded with this codec. + * @param bitsAllocated current value of Bits Allocated + * @param bitsStored current value of Bits Stored + * @return value of BitsAllocated after decompression, 0 if no decompression possible + */ + virtual Uint16 decodedBitsAllocated( + Uint16 bitsAllocated, + Uint16 bitsStored) const; + /** determine color model of the decompressed image * @param fromParam representation parameter of current compressed * representation, may be NULL diff --git a/dcmjpls/libcharls/encodstr.h b/dcmjpls/libcharls/encodstr.h index f4977005..be7a970f 100644 --- a/dcmjpls/libcharls/encodstr.h +++ b/dcmjpls/libcharls/encodstr.h @@ -1,6 +1,6 @@ -// -// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use. -// +// +// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use. +// #ifndef CHARLS_ENCODERSTRATEGY #define CHARLS_ENCODERSTRATEGY @@ -24,16 +24,16 @@ public: bitpos(0), _isFFWritten(false), _bytesWritten(0) - + { } - virtual ~EncoderStrategy() + virtual ~EncoderStrategy() { } LONG PeekByte(); - + void OnLineBegin(LONG cpixel, void* ptypeBuffer, LONG pixelStride) { _processLine->NewLineRequested(ptypeBuffer, cpixel, pixelStride); @@ -42,7 +42,7 @@ public: void OnLineEnd(LONG /*cpixel*/, void* /*ptypeBuffer*/, LONG /*pixelStride*/) { } virtual void SetPresets(const JlsCustomParameters& presets) = 0; - + virtual size_t EncodeScan(const void* rawData, BYTE **ptr, size_t *size, size_t offset, bool compare) = 0; protected: @@ -80,7 +80,7 @@ protected: valcurrent |= value >> -bitpos; Flush(); - + // A second flush may be required if extra marker-detect bits were needed and not all bits could be written. if (bitpos < 0) { @@ -101,7 +101,7 @@ protected: AppendToBitStream(0, (bitpos - 1) % 8); else AppendToBitStream(0, bitpos % 8); - + Flush(); ASSERT(bitpos == 0x20); } @@ -125,27 +125,27 @@ protected: { write(BYTE(valcurrent >> 24)); _isFFWritten = (*_position)[_current_offset - 1] == 0xFF; - valcurrent = valcurrent << 8; + valcurrent = valcurrent << 8; bitpos += 8; } } - + } - size_t GetLength() - { - return _bytesWritten - (bitpos -32)/8; + size_t GetLength() + { + return _bytesWritten - (bitpos -32)/8; } inlinehint void AppendOnesToBitStream(LONG length) { - AppendToBitStream((1 << length) - 1, length); + AppendToBitStream((1 << length) - 1, length); } - OFunique_ptr _qdecoder; + OFunique_ptr _qdecoder; protected: JlsParameters _info; @@ -154,11 +154,7 @@ private: static BYTE *re_alloc(BYTE *old_ptr, size_t *old_size) { size_t new_size = *old_size * 2; -#ifdef HAVE_STD__NOTHROW BYTE *new_ptr = new(std::nothrow) BYTE[new_size]; -#else - BYTE *new_ptr = new BYTE[new_size]; -#endif if (new_ptr == NULL) { throw alloc_fail(); } @@ -185,7 +181,7 @@ private: unsigned int valcurrent; LONG bitpos; - + // encoding BYTE **_position; size_t *_size; diff --git a/dcmjpls/libcharls/lltraits.h b/dcmjpls/libcharls/lltraits.h index e08249ca..851e5829 100644 --- a/dcmjpls/libcharls/lltraits.h +++ b/dcmjpls/libcharls/lltraits.h @@ -54,11 +54,10 @@ struct LosslessTraitsImplT }; -// For some weird reason MSVC6 doesn't like these templates. // xlC (compiler on AIX) tries to instantiate Triple > which // causes compiler errors and is wrong (other compilers don't instantiate it). -#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__xlC__) -# define DISABLE_SPECIALIZATIONS +#ifdef __xlC__ +#define DISABLE_SPECIALIZATIONS #else template diff --git a/dcmjpls/libcharls/scan.h b/dcmjpls/libcharls/scan.h index b4dea20d..f1309810 100644 --- a/dcmjpls/libcharls/scan.h +++ b/dcmjpls/libcharls/scan.h @@ -1,6 +1,6 @@ -// -// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use. -// +// +// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use. +// #ifndef CHARLS_SCAN #define CHARLS_SCAN @@ -11,7 +11,7 @@ #include "lokuptbl.h" -// This file contains the code for handling a "scan". Usually an image is encoded as a single scan. +// This file contains the code for handling a "scan". Usually an image is encoded as a single scan. #include DCMTK_DIAGNOSTIC_IGNORE_CONST_EXPRESSION_WARNING @@ -21,10 +21,10 @@ extern OFVector rgquant10Ll; extern OFVector rgquant12Ll; extern OFVector rgquant16Ll; // -// Apply +// Apply // inlinehint LONG ApplySign(LONG i, LONG sign) -{ return (sign ^ i) - sign; } +{ return (sign ^ i) - sign; } @@ -58,20 +58,20 @@ inlinehint LONG GetPredictedValue(LONG Ra, LONG Rb, LONG Rc) inlinehint LONG GetPredictedValue(LONG Ra, LONG Rb, LONG Rc) { - // sign trick reduces the number of if statements (branches) + // sign trick reduces the number of if statements (branches) LONG sgn = BitWiseSign(Rb - Ra); - // is Ra between Rc and Rb? + // is Ra between Rc and Rb? if ((sgn ^ (Rc - Ra)) < 0) { return Rb; - } + } else if ((sgn ^ (Rb - Rc)) < 0) { return Ra; } - // default case, valid if Rc element of [Ra,Rb] + // default case, valid if Rc element of [Ra,Rb] return Ra + Rb - Rc; } @@ -110,7 +110,7 @@ public: public: - JlsCodec(const TRAITS& inTraits, const JlsParameters& info) : STRATEGY(info), + JlsCodec(const TRAITS& inTraits, const JlsParameters& info) : STRATEGY(info), traits(inTraits), _rect(), _width(0), @@ -120,13 +120,13 @@ public: _RUNindex(0), _pquant(0), _bCompare(0) - + { if (Info().ilv == ILV_NONE) { Info().components = 1; } - } + } void SetPresets(const JlsCustomParameters& presets) @@ -135,9 +135,9 @@ public: InitParams(presets.T1 != 0 ? presets.T1 : presetDefault.T1, presets.T2 != 0 ? presets.T2 : presetDefault.T2, - presets.T3 != 0 ? presets.T3 : presetDefault.T3, + presets.T3 != 0 ? presets.T3 : presetDefault.T3, presets.RESET != 0 ? presets.RESET : presetDefault.RESET); - } + } bool IsInterleaved() @@ -155,13 +155,13 @@ public: signed char QuantizeGratientOrg(LONG Di); inlinehint LONG QuantizeGratient(LONG Di) - { + { ASSERT(QuantizeGratientOrg(Di) == *(_pquant + Di)); - return *(_pquant + Di); + return *(_pquant + Di); } void InitQuantizationLUT(); - + LONG DecodeValue(LONG k, LONG limit, LONG qbpp); inlinehint void EncodeMappedValue(LONG k, LONG mappedError, LONG limit); @@ -216,27 +216,27 @@ public: { LONG sign = BitWiseSign(Qs); JlsContext& ctx = _contexts[ApplySign(Qs, sign)]; - LONG k = ctx.GetGolomb(); - LONG Px = traits.CorrectPrediction(pred + ApplySign(ctx.C, sign)); + LONG k = ctx.GetGolomb(); + LONG Px = traits.CorrectPrediction(pred + ApplySign(ctx.C, sign)); LONG ErrVal; const Code& code = decodingTables[k].Get(STRATEGY::PeekByte()); if (code.GetLength() != 0) { STRATEGY::Skip(code.GetLength()); - ErrVal = code.GetValue(); + ErrVal = code.GetValue(); ASSERT(ABS(ErrVal) < 65535); } else { - ErrVal = UnMapErrVal(DecodeValue(k, traits.LIMIT, traits.qbpp)); + ErrVal = UnMapErrVal(DecodeValue(k, traits.LIMIT, traits.qbpp)); if (ABS(ErrVal) > 65535) throw JlsException(InvalidCompressedData); - } + } ErrVal = ErrVal ^ ((traits.NEAR == 0) ? ctx.GetErrorCorrection(k) : 0); - ctx.UpdateVariables(ErrVal, traits.NEAR, traits.RESET); + ctx.UpdateVariables(ErrVal, traits.NEAR, traits.RESET); ErrVal = ApplySign(ErrVal, sign); - return traits.ComputeReconstructedSample(Px, ErrVal); + return traits.ComputeReconstructedSample(Px, ErrVal); } @@ -245,7 +245,7 @@ public: LONG sign = BitWiseSign(Qs); JlsContext& ctx = _contexts[ApplySign(Qs, sign)]; LONG k = ctx.GetGolomb(); - LONG Px = traits.CorrectPrediction(pred + ApplySign(ctx.C, sign)); + LONG Px = traits.CorrectPrediction(pred + ApplySign(ctx.C, sign)); LONG ErrVal = traits.ComputeErrVal(ApplySign(x - Px, sign)); @@ -270,16 +270,16 @@ public: size_t DecodeScan(void* rawData, const JlsRect& size, BYTE **buf, size_t *buf_size, size_t offset, bool bCompare); protected: - // codec parameters + // codec parameters TRAITS traits; JlsRect _rect; int _width; - LONG T1; + LONG T1; LONG T2; - LONG T3; + LONG T3; // compression context - JlsContext _contexts[365]; + JlsContext _contexts[365]; CContextRunMode _contextRunmode[2]; LONG _RUNindex; PIXEL* _previousLine; // previous line ptr @@ -309,7 +309,7 @@ CTable InitTable(LONG k) CTable table; short nerr; for (nerr = 0; ; nerr++) - { + { // Q is not used when k != 0 LONG merrval = GetMappedErrVal(nerr);//, k, -1); OFPair paircode = CreateEncodedValue(k, merrval); @@ -321,7 +321,7 @@ CTable InitTable(LONG k) } for (nerr = -1; ; nerr--) - { + { // Q is not used when k != 0 LONG merrval = GetMappedErrVal(nerr);//, k, -1); OFPair paircode = CreateEncodedValue(k, merrval); @@ -364,7 +364,7 @@ inlinehint void JlsCodec::EncodeMappedValue(LONG k, LONG mapped if (highbits + 1 > 31) { STRATEGY::AppendToBitStream(0, highbits / 2); - highbits = highbits - highbits / 2; + highbits = highbits - highbits / 2; } STRATEGY::AppendToBitStream(1, highbits + 1); STRATEGY::AppendToBitStream((mappedError & ((1 << k) - 1)), k); @@ -374,11 +374,11 @@ inlinehint void JlsCodec::EncodeMappedValue(LONG k, LONG mapped if (limit - traits.qbpp > 31) { STRATEGY::AppendToBitStream(0, 31); - STRATEGY::AppendToBitStream(1, limit - traits.qbpp - 31); + STRATEGY::AppendToBitStream(1, limit - traits.qbpp - 31); } else { - STRATEGY::AppendToBitStream(1, limit - traits.qbpp); + STRATEGY::AppendToBitStream(1, limit - traits.qbpp); } STRATEGY::AppendToBitStream((mappedError - 1) & ((1 << traits.qbpp) - 1), traits.qbpp); } @@ -389,33 +389,33 @@ inlinehint void JlsCodec::EncodeMappedValue(LONG k, LONG mapped template void JlsCodec::InitQuantizationLUT() { - // for lossless mode with default parameters, we have precomputed te luts for bitcounts 8,10,12 and 16 + // for lossless mode with default parameters, we have precomputed te luts for bitcounts 8,10,12 and 16 if (traits.NEAR == 0 && traits.MAXVAL == (1 << traits.bpp) - 1) { JlsCustomParameters presets = ComputeDefault(traits.MAXVAL, traits.NEAR); if (presets.T1 == T1 && presets.T2 == T2 && presets.T3 == T3) { - if (traits.bpp == 8) + if (traits.bpp == 8) { - _pquant = &rgquant8Ll[rgquant8Ll.size() / 2 ]; + _pquant = &rgquant8Ll[rgquant8Ll.size() / 2 ]; return; } - if (traits.bpp == 10) + if (traits.bpp == 10) { - _pquant = &rgquant10Ll[rgquant10Ll.size() / 2 ]; + _pquant = &rgquant10Ll[rgquant10Ll.size() / 2 ]; return; - } - if (traits.bpp == 12) + } + if (traits.bpp == 12) { - _pquant = &rgquant12Ll[rgquant12Ll.size() / 2 ]; + _pquant = &rgquant12Ll[rgquant12Ll.size() / 2 ]; return; - } - if (traits.bpp == 16) + } + if (traits.bpp == 16) { - _pquant = &rgquant16Ll[rgquant16Ll.size() / 2 ]; + _pquant = &rgquant16Ll[rgquant16Ll.size() / 2 ]; return; - } - } + } + } } LONG RANGE = 1 << traits.bpp; @@ -453,7 +453,7 @@ template LONG JlsCodec::DecodeRIError(CContextRunMode& ctx) { LONG k = ctx.GetGolomb(); - LONG EMErrval = DecodeValue(k, traits.LIMIT - J[_RUNindex]-1, traits.qbpp); + LONG EMErrval = DecodeValue(k, traits.LIMIT - J[_RUNindex]-1, traits.qbpp); LONG Errval = ctx.ComputeErrVal(EMErrval + ctx._nRItype, k); ctx.UpdateVariables(Errval, EMErrval); return Errval; @@ -466,7 +466,7 @@ void JlsCodec::EncodeRIError(CContextRunMode& ctx, LONG Errval) { LONG k = ctx.GetGolomb(); bool map = ctx.ComputeMap(Errval, k); - LONG EMErrval = 2 * ABS(Errval) - ctx._nRItype - map; + LONG EMErrval = 2 * ABS(Errval) - ctx._nRItype - map; ASSERT(Errval == ctx.ComputeErrVal(EMErrval + ctx._nRItype, k)); EncodeMappedValue(k, EMErrval, traits.LIMIT-J[_RUNindex]-1); @@ -476,7 +476,7 @@ void JlsCodec::EncodeRIError(CContextRunMode& ctx, LONG Errval) template Triplet JlsCodec::DecodeRIPixel(Triplet Ra, Triplet Rb) -{ +{ LONG Errval1 = DecodeRIError(_contextRunmode[0]); LONG Errval2 = DecodeRIError(_contextRunmode[0]); LONG Errval3 = DecodeRIError(_contextRunmode[0]); @@ -513,18 +513,18 @@ Triplet JlsCodec::EncodeRIPixel(Trip template void JlsCodec::EncodeRunPixels(LONG runLength, bool endOfLine) { - while (runLength >= LONG(1 << J[_RUNindex])) + while (runLength >= LONG(1 << J[_RUNindex])) { STRATEGY::AppendOnesToBitStream(1); runLength = runLength - LONG(1 << J[_RUNindex]); IncrementRunIndex(); } - if (endOfLine) + if (endOfLine) { - if (runLength != 0) + if (runLength != 0) { - STRATEGY::AppendOnesToBitStream(1); + STRATEGY::AppendOnesToBitStream(1); } } else @@ -556,7 +556,7 @@ LONG JlsCodec::DecodeRunPixels(PIXEL Ra, PIXEL* startPos, LONG if (index != cpixelMac) { - // incomplete run + // incomplete run index += (J[_RUNindex] > 0) ? STRATEGY::ReadValue(J[_RUNindex]) : 0; } @@ -566,7 +566,7 @@ LONG JlsCodec::DecodeRunPixels(PIXEL Ra, PIXEL* startPos, LONG for (LONG i = 0; i < index; ++i) { startPos[i] = Ra; - } + } return index; } @@ -582,7 +582,7 @@ LONG JlsCodec::DoRunMode(LONG index, EncoderStrategy*) LONG runLength = 0; - while (traits.IsNear(ptypeCurX[runLength],Ra)) + while (traits.IsNear(ptypeCurX[runLength],Ra)) { ptypeCurX[runLength] = Ra; runLength++; @@ -629,14 +629,24 @@ void JlsCodec::DoLine(SAMPLE*) LONG index = 0; LONG Rb = _previousLine[index-1]; LONG Rd = _previousLine[index]; + LONG RANGE_UPPER = 1 << traits.bpp; + LONG RANGE_LOWER = - RANGE_UPPER; while(index < _width) - { + { LONG Ra = _currentLine[index -1]; LONG Rc = Rb; Rb = Rd; Rd = _previousLine[index + 1]; + // make sure that values are not out of range + if ( (Rd - Rb < RANGE_LOWER) || (Rd - Rb > RANGE_UPPER) + || (Rb - Rc < RANGE_LOWER) || (Rb - Rc > RANGE_UPPER) + || (Rc - Ra < RANGE_LOWER) || (Rc - Ra > RANGE_UPPER)) + { + throw JlsException(InvalidCompressedData); + } + LONG Qs = ComputeContextID(QuantizeGratient(Rd - Rb), QuantizeGratient(Rb - Rc), QuantizeGratient(Rc - Ra)); if (Qs != 0) @@ -648,8 +658,8 @@ void JlsCodec::DoLine(SAMPLE*) { index += DoRunMode(index, (STRATEGY*)(NULL)); Rb = _previousLine[index-1]; - Rd = _previousLine[index]; - } + Rd = _previousLine[index]; + } } } @@ -661,7 +671,7 @@ void JlsCodec::DoLine(Triplet*) { LONG index = 0; while(index < _width) - { + { Triplet Ra = _currentLine[index -1]; Triplet Rc = _previousLine[index-1]; Triplet Rb = _previousLine[index]; @@ -671,7 +681,7 @@ void JlsCodec::DoLine(Triplet*) LONG Qs2 = ComputeContextID(QuantizeGratient(Rd.v2 - Rb.v2), QuantizeGratient(Rb.v2 - Rc.v2), QuantizeGratient(Rc.v2 - Ra.v2)); LONG Qs3 = ComputeContextID(QuantizeGratient(Rd.v3 - Rb.v3), QuantizeGratient(Rb.v3 - Rc.v3), QuantizeGratient(Rc.v3 - Ra.v3)); - + if (Qs1 == 0 && Qs2 == 0 && Qs3 == 0) { index += DoRunMode(index, (STRATEGY*)(NULL)); @@ -684,19 +694,19 @@ void JlsCodec::DoLine(Triplet*) Rx.v3 = DoRegular(Qs3, _currentLine[index].v3, GetPredictedValue(Ra.v3, Rb.v3, Rc.v3), (STRATEGY*)(NULL)); _currentLine[index] = Rx; index++; - } + } } } -// DoScan: Encodes or decodes a scan. +// DoScan: Encodes or decodes a scan. // In ILV_SAMPLE mode, multiple components are handled in DoLine // In ILV_LINE mode, a call do DoLine is made for every component -// In ILV_NONE mode, DoScan is called for each component +// In ILV_NONE mode, DoScan is called for each component template void JlsCodec::DoScan(BYTE **ptr, size_t *size, size_t offset) -{ +{ _width = Info().width; STRATEGY::Init(ptr, size, offset); @@ -706,11 +716,11 @@ void JlsCodec::DoScan(BYTE **ptr, size_t *size, size_t offset) OFVector vectmp(2 * components * pixelstride); OFVector rgRUNindex(components); - + for (LONG line = 0; line < Info().height; ++line) { - _previousLine = &vectmp[1]; - _currentLine = &vectmp[1 + components * pixelstride]; + _previousLine = &vectmp[1]; + _currentLine = &vectmp[1 + components * pixelstride]; if ((line & 1) == 1) { PIXEL *tmp = _previousLine; @@ -724,17 +734,17 @@ void JlsCodec::DoScan(BYTE **ptr, size_t *size, size_t offset) for (int component = 0; component < components; ++component) { _RUNindex = rgRUNindex[component]; - + // initialize edge pixels used for prediction _previousLine[_width] = _previousLine[_width - 1]; _currentLine[-1] = _previousLine[0]; DoLine((PIXEL*) NULL); // dummy arg for overload resolution - + rgRUNindex[component] = _RUNindex; _previousLine += pixelstride; _currentLine += pixelstride; } - + if (_rect.Y <= line && line < _rect.Y + _rect.Height) { STRATEGY::OnLineEnd(_rect.Width, _currentLine + _rect.X - (components * pixelstride), pixelstride); @@ -754,7 +764,7 @@ ProcessLine* JlsCodec::CreateProcess(void* pvoidOut) return new PostProcesSingleComponent(pvoidOut, Info(), sizeof(typename TRAITS::PIXEL)); if (Info().colorTransform == 0) - return new ProcessTransformed >(pvoidOut, Info(), TransformNone()); + return new ProcessTransformed >(pvoidOut, Info(), TransformNone()); if (Info().bitspersample == sizeof(SAMPLE)*8) { @@ -765,7 +775,7 @@ ProcessLine* JlsCodec::CreateProcess(void* pvoidOut) case COLORXFORM_HP3 : return new ProcessTransformed >(pvoidOut, Info(), TransformHp3()); break; default: throw JlsException(UnsupportedColorTransform); } - } + } else if (Info().bitspersample > 8) { int shift = 16 - Info().bitspersample; @@ -796,7 +806,7 @@ size_t JlsCodec::EncodeScan(const void* rawData, BYTE **ptr, si } DoScan(ptr, size, offset); - + return STRATEGY::GetLength(); } @@ -827,7 +837,7 @@ size_t JlsCodec::DecodeScan(void* rawData, const JlsRect& rect, _rect = rect; DoScan(ptr, size, offset + readBytes); - + return STRATEGY::GetCurBytePos() - (*ptr + offset); } diff --git a/dcmjpls/libcharls/streams.h b/dcmjpls/libcharls/streams.h index 1e7f123b..de71e0e5 100644 --- a/dcmjpls/libcharls/streams.h +++ b/dcmjpls/libcharls/streams.h @@ -1,6 +1,6 @@ -// -// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use. -// +// +// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use. +// #ifndef CHARLS_STREAMS #define CHARLS_STREAMS @@ -10,7 +10,7 @@ -// This file defines JPEG-LS streams: The header and the actual pixel data. Header markers have fixed length, the pixeldata not. +// This file defines JPEG-LS streams: The header and the actual pixel data. Header markers have fixed length, the pixeldata not. @@ -18,112 +18,108 @@ class JpegSegment; enum JPEGLS_ColorXForm { - // default (RGB) - COLORXFORM_NONE = 0, + // default (RGB) + COLORXFORM_NONE = 0, - // Color transforms as defined by HP - COLORXFORM_HP1, - COLORXFORM_HP2, - COLORXFORM_HP3, + // Color transforms as defined by HP + COLORXFORM_HP1, + COLORXFORM_HP2, + COLORXFORM_HP3, - // Defined by HP but not supported by CharLS - COLORXFORM_RGB_AS_YUV_LOSSY, - COLORXFORM_MATRIX + // Defined by HP but not supported by CharLS + COLORXFORM_RGB_AS_YUV_LOSSY, + COLORXFORM_MATRIX }; - + // // JLSOutputStream: minimal implementation to write JPEG header streams // class JLSOutputStream { - friend class JpegMarkerSegment; - friend class JpegImageDataSegment; + friend class JpegMarkerSegment; + friend class JpegImageDataSegment; public: - JLSOutputStream(); - virtual ~JLSOutputStream(); + JLSOutputStream(); + virtual ~JLSOutputStream(); - void Init(Size size, LONG bitsPerSample, LONG ccomp); - void AddScan(const void* compareData, const JlsParameters* pparams); - void AddLSE(const JlsCustomParameters* pcustom); - void AddColorTransform(int i); - size_t GetBytesWritten() - { return _cbytesWritten; } + void Init(Size size, LONG bitsPerSample, LONG ccomp); + void AddScan(const void* compareData, const JlsParameters* pparams); + void AddLSE(const JlsCustomParameters* pcustom); + void AddColorTransform(int i); + size_t GetBytesWritten() + { return _cbytesWritten; } - size_t Write(BYTE **ptr, size_t *size, size_t offset); + size_t Write(BYTE **ptr, size_t *size, size_t offset); - BYTE **get_pos() { return _position; } + BYTE **get_pos() { return _position; } - size_t *get_size() { return _size; } + size_t *get_size() { return _size; } - size_t get_offset() { return _current_offset; } + size_t get_offset() { return _current_offset; } - void EnableCompare(bool bCompare) - { _bCompare = bCompare; } + void EnableCompare(bool bCompare) + { _bCompare = bCompare; } private: - void WriteByte(BYTE val) - { - ASSERT(!_bCompare || (*_position)[_current_offset] == val); - - if (_current_offset == *_size) { - *_position = re_alloc(*_position, _size); - } - - (*_position)[_current_offset++] = val; - - _cbytesWritten++; - } - - void WriteBytes(const OFVector& rgbyte) - { - for (size_t i = 0; i < rgbyte.size(); ++i) - { - WriteByte(rgbyte[i]); - } - } - - void WriteWord(USHORT val) - { - WriteByte(BYTE(val / 0x100)); - WriteByte(BYTE(val % 0x100)); - } - - void seek(size_t n) - { - _cbytesWritten += n; - _current_offset += n; - } - - bool _bCompare; + void WriteByte(BYTE val) + { + ASSERT(!_bCompare || (*_position)[_current_offset] == val); + + if (_current_offset == *_size) { + *_position = re_alloc(*_position, _size); + } + + (*_position)[_current_offset++] = val; + + _cbytesWritten++; + } + + void WriteBytes(const OFVector& rgbyte) + { + for (size_t i = 0; i < rgbyte.size(); ++i) + { + WriteByte(rgbyte[i]); + } + } + + void WriteWord(USHORT val) + { + WriteByte(BYTE(val / 0x100)); + WriteByte(BYTE(val % 0x100)); + } + + void seek(size_t n) + { + _cbytesWritten += n; + _current_offset += n; + } + + bool _bCompare; private: - static BYTE *re_alloc(BYTE *old_ptr, size_t *old_size) - { - size_t new_size = *old_size * 2; -#ifdef HAVE_STD__NOTHROW - BYTE *new_ptr = new BYTE[new_size]; -#else - BYTE *new_ptr = new BYTE[new_size]; -#endif - if (new_ptr == NULL) { - throw alloc_fail(); - } + static BYTE *re_alloc(BYTE *old_ptr, size_t *old_size) + { + size_t new_size = *old_size * 2; + BYTE *new_ptr = new(std::nothrow) BYTE[new_size]; + if (new_ptr == NULL) { + throw alloc_fail(); + } - OFBitmanipTemplate::copyMem(old_ptr, new_ptr, *old_size); + OFBitmanipTemplate::copyMem(old_ptr, new_ptr, *old_size); - delete[] old_ptr; + delete[] old_ptr; - *old_size = new_size; + *old_size = new_size; - return new_ptr; - } + return new_ptr; + } - BYTE **_position; - size_t *_size; - size_t _current_offset; - size_t _cbytesWritten; - LONG _icompLast; - OFVector _segments; + BYTE **_position; + size_t *_size; + size_t _current_offset; + size_t _cbytesWritten; + LONG _icompLast; + OFVector _segments; }; @@ -133,51 +129,51 @@ private: class JLSInputStream { public: - JLSInputStream(const BYTE* pdata, size_t cbyteLength); + JLSInputStream(const BYTE* pdata, size_t cbyteLength); + + size_t GetBytesRead() + { return _cbyteOffset; } - size_t GetBytesRead() - { return _cbyteOffset; } + const JlsParameters& GetMetadata() const + { return _info; } - const JlsParameters& GetMetadata() const - { return _info; } + const JlsCustomParameters& GetCustomPreset() const + { return _info.custom; } - const JlsCustomParameters& GetCustomPreset() const - { return _info.custom; } + void Read(void* pvoid, size_t cbyteAvailable); + void ReadHeader(); - void Read(void* pvoid, size_t cbyteAvailable); - void ReadHeader(); - - void EnableCompare(bool bCompare) - { _bCompare = bCompare; } + void EnableCompare(bool bCompare) + { _bCompare = bCompare; } - void SetInfo(JlsParameters* info) { _info = *info; } + void SetInfo(JlsParameters* info) { _info = *info; } - void SetRect(JlsRect rect) { _rect = rect; } + void SetRect(JlsRect rect) { _rect = rect; } private: - void ReadPixels(void* pvoid, size_t cbyteAvailable); - void ReadScan(void*); - void ReadStartOfScan(); - void ReadPresetParameters(); - void ReadComment(); - void ReadStartOfFrame(); - BYTE ReadByte(); - int ReadWord(); - void ReadNBytes(OFVector& dst, int byteCount); - - // JFIF - void ReadJfif(); - // Color Transform Application Markers & Code Stream (HP extension) - void ReadColorSpace(); - void ReadColorXForm(); - + void ReadPixels(void* pvoid, size_t cbyteAvailable); + void ReadScan(void*); + void ReadStartOfScan(); + void ReadPresetParameters(); + void ReadComment(); + void ReadStartOfFrame(); + BYTE ReadByte(); + int ReadWord(); + void ReadNBytes(OFVector& dst, int byteCount); + + // JFIF + void ReadJfif(); + // Color Transform Application Markers & Code Stream (HP extension) + void ReadColorSpace(); + void ReadColorXForm(); + private: - const BYTE* _pdata; - size_t _cbyteOffset; - size_t _cbyteLength; - bool _bCompare; - JlsParameters _info; - JlsRect _rect; + const BYTE* _pdata; + size_t _cbyteOffset; + size_t _cbyteLength; + bool _bCompare; + JlsParameters _info; + JlsRect _rect; }; diff --git a/dcmjpls/libsrc/djcodecd.cc b/dcmjpls/libsrc/djcodecd.cc index ca549afd..27f576db 100644 --- a/dcmjpls/libsrc/djcodecd.cc +++ b/dcmjpls/libsrc/djcodecd.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2007-2024, OFFIS e.V. + * Copyright (C) 2007-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -76,6 +76,24 @@ OFBool DJLSDecoderBase::canChangeCoding( } +Uint16 DJLSDecoderBase::decodedBitsAllocated( + Uint16 /* bitsAllocated */, + Uint16 bitsStored) const +{ + // this codec does not support images with less than 2 bits per sample + if (bitsStored < 2) return 0; + + // for images with 2..8 bits per sample, BitsAllocated will be 8 + if (bitsStored <= 8) return 8; + + // for images with 9..16 bits per sample, BitsAllocated will be 16 + if (bitsStored <= 16) return 16; + + // this codec does not support images with more than 16 bits per sample + return 0; +} + + OFCondition DJLSDecoderBase::decode( const DcmRepresentationParameter * /* fromRepParam */, DcmPixelSequence * pixSeq, @@ -186,7 +204,7 @@ OFCondition DJLSDecoderBase::decode( { DCMJPLS_DEBUG("JPEG-LS decoder processes frame " << (currentFrame+1)); - result = decodeFrame(pixSeq, djcp, dataset, currentFrame, currentItem, pixeldata8, frameSize, + result = decodeFrameNoSwap(pixSeq, djcp, dataset, currentFrame, currentItem, pixeldata8, frameSize, imageFrames, imageColumns, imageRows, imageSamplesPerPixel, bytesPerSample); // check if we should enforce "one fragment per frame" while @@ -215,6 +233,15 @@ OFCondition DJLSDecoderBase::decode( result = ((DcmItem *)dataset)->putAndInsertString(DCM_NumberOfFrames, numBuf); } + if (result.good()) + { + // decompression is complete, finally adjust byte order if necessary + if (bytesPerSample == 1) // we're writing bytes into words + { + result = swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, pixeldata16, OFstatic_cast(Uint32, totalSize), sizeof(Uint16)); + } + } + if (result.good() && (dataset->ident() == EVR_dataset)) { DcmItem *ditem = OFreinterpret_cast(DcmItem*, dataset); @@ -315,7 +342,7 @@ OFCondition DJLSDecoderBase::decodeFrame( return result; } -OFCondition DJLSDecoderBase::decodeFrame( +OFCondition DJLSDecoderBase::decodeFrameNoSwap( DcmPixelSequence * fromPixSeq, const DJLSCodecParameter *cp, DcmItem *dataset, @@ -472,16 +499,6 @@ OFCondition DJLSDecoderBase::decodeFrame( } } - if (result.good()) - { - // decompression is complete, finally adjust byte order if necessary - if (bytesPerSample == 1) // we're writing bytes into words - { - result = swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, buffer, - bufSize, sizeof(Uint16)); - } - } - // update planar configuration if we are decoding a color image if (result.good() && (imageSamplesPerPixel > 1)) { @@ -493,6 +510,35 @@ OFCondition DJLSDecoderBase::decodeFrame( return result; } +OFCondition DJLSDecoderBase::decodeFrame( + DcmPixelSequence * fromPixSeq, + const DJLSCodecParameter *cp, + DcmItem *dataset, + Uint32 frameNo, + Uint32& currentItem, + void * buffer, + Uint32 bufSize, + Sint32 imageFrames, + Uint16 imageColumns, + Uint16 imageRows, + Uint16 imageSamplesPerPixel, + Uint16 bytesPerSample) +{ + OFCondition result = decodeFrameNoSwap(fromPixSeq, cp, dataset, frameNo, currentItem, buffer, bufSize, imageFrames, imageColumns, imageRows, imageSamplesPerPixel, bytesPerSample); + if (result.good()) + { + // decompression is complete, finally adjust byte order if necessary + if (bytesPerSample == 1) // we're writing bytes into words + { + if ((gLocalByteOrder == EBO_BigEndian) && (bufSize & 1)) + { + DCMJPLS_WARN("Size of frame buffer is odd, cannot correct byte order for last pixel value"); + } + result = swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, buffer, bufSize, sizeof(Uint16)); + } + } + return result; +} OFCondition DJLSDecoderBase::encode( const Uint16 * /* pixelData */, diff --git a/dcmjpls/libsrc/djcodece.cc b/dcmjpls/libsrc/djcodece.cc index d6acbf05..b6ea5417 100644 --- a/dcmjpls/libsrc/djcodece.cc +++ b/dcmjpls/libsrc/djcodece.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2007-2024, OFFIS e.V. + * Copyright (C) 2007-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -55,15 +55,9 @@ #include "intrface.h" BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include /* for O_RDONLY */ -#endif -#ifdef HAVE_SYS_TYPES_H #include /* required for sys/stat.h */ -#endif -#ifdef HAVE_SYS_STAT_H #include /* for stat, fstat */ -#endif END_EXTERN_C @@ -100,6 +94,14 @@ OFBool DJLSEncoderBase::canChangeCoding( } +Uint16 DJLSEncoderBase::decodedBitsAllocated( + Uint16 /* bitsAllocated */, + Uint16 /* bitsStored */) const +{ + return 0; +} + + OFCondition DJLSEncoderBase::decode( const DcmRepresentationParameter * /* fromRepParam */, DcmPixelSequence * /* pixSeq */, @@ -666,7 +668,7 @@ OFCondition DJLSEncoderBase::compressRawFrame( jls_params.width = width; jls_params.allowedlossyerror = 0; // must be zero for raw mode jls_params.outputBgr = false; - // No idea what this one does, but I don't think DICOM says anything about it + // color transformation is a non-standard HP/JPEG-LS extension jls_params.colorTransform = 0; // Unset: jls_params.jfif (thumbnail, dpi) @@ -1015,12 +1017,17 @@ OFCondition DJLSEncoderBase::compressCookedFrame( case EPR_Sint8: { // image representation is 8 bit signed or unsigned + Uint8 mask = 0xFF >> (8-depth); if (samplesPerPixel == 1) { const Uint8 *yv = OFreinterpret_cast(const Uint8 *, planes[0]) + framesize * frame; buffer_size = framesize; buffer = new Uint8[buffer_size]; - memcpy(buffer, yv, framesize); + for (size_t i=0; i < framesize; ++i) + { + buffer[i] = *yv & mask; + yv++; + } } else { @@ -1036,9 +1043,9 @@ OFCondition DJLSEncoderBase::compressCookedFrame( { for (int col=width; col; --col) { - buffer[i++] = *rv; - buffer[i++] = *gv; - buffer[i++] = *bv; + buffer[i++] = *rv & mask; + buffer[i++] = *gv & mask; + buffer[i++] = *bv & mask; rv++; gv++; @@ -1052,12 +1059,23 @@ OFCondition DJLSEncoderBase::compressCookedFrame( case EPR_Sint16: { // image representation is 16 bit signed or unsigned + Uint16 mask = 0xFFFF >> (16-depth); if (samplesPerPixel == 1) { const Uint16 *yv = OFreinterpret_cast(const Uint16 *, planes[0]) + framesize * frame; - buffer_size = framesize*sizeof(Uint16); - buffer = new Uint8[buffer_size]; - memcpy(buffer, yv, buffer_size); + + buffer_size = framesize; + Uint16 *buffer16 = new Uint16[buffer_size]; + buffer = OFreinterpret_cast(Uint8 *, buffer16); + + // Convert to byte count + buffer_size *= 2; + + for (size_t i=0; i < framesize; ++i) + { + buffer16[i] = *yv & mask; + yv++; + } } else { @@ -1077,9 +1095,9 @@ OFCondition DJLSEncoderBase::compressCookedFrame( { for (int col=width; col; --col) { - buffer16[i++] = *rv; - buffer16[i++] = *gv; - buffer16[i++] = *bv; + buffer16[i++] = *rv & mask; + buffer16[i++] = *gv & mask; + buffer16[i++] = *bv & mask; rv++; gv++; @@ -1104,7 +1122,7 @@ OFCondition DJLSEncoderBase::compressCookedFrame( jls_params.allowedlossyerror = nearLosslessDeviation; jls_params.outputBgr = false; jls_params.bitspersample = depth; - // No idea what this one does, but I don't think DICOM says anything about it + // color transformation is a non-standard HP/JPEG-LS extension jls_params.colorTransform = 0; // This was already checked for a sane value above @@ -1168,7 +1186,7 @@ OFCondition DJLSEncoderBase::compressCookedFrame( { // 'compressed_buffer_size' now contains the size of the compressed data in buffer compressedSize = OFstatic_cast(unsigned long, bytesWritten); - fixPaddingIfNecessary(OFstatic_cast(Uint8 *, buffer), compressed_buffer_size, compressedSize, djcp->getUseFFbitstreamPadding()); + fixPaddingIfNecessary(OFstatic_cast(Uint8 *, compressed_buffer), compressed_buffer_size, compressedSize, djcp->getUseFFbitstreamPadding()); result = pixelSequence->storeCompressedFrame(offsetList, compressed_buffer, compressedSize, fragmentSize); } diff --git a/dcmnet/apps/CMakeLists.txt b/dcmnet/apps/CMakeLists.txt index 8ac501c4..f771154c 100644 --- a/dcmnet/apps/CMakeLists.txt +++ b/dcmnet/apps/CMakeLists.txt @@ -26,10 +26,10 @@ if(WITH_OPENSSL) endif() # make sure executables are linked to the corresponding libraries -foreach(PROGRAM dcmrecv dcmsend echoscu findscu getscu movescu storescp storescu termscu) - DCMTK_TARGET_LINK_MODULES(${PROGRAM} dcmnet dcmdata oflog ofstd) +foreach(PROGRAM dcmsend termscu) + DCMTK_TARGET_LINK_MODULES(${PROGRAM} dcmnet) endforeach() -foreach(PROGRAM dcmrecv echoscu findscu storescp storescu getscu) +foreach(PROGRAM dcmrecv echoscu findscu storescp storescu getscu movescu) DCMTK_TARGET_LINK_MODULES(${PROGRAM} dcmtls) endforeach() diff --git a/dcmnet/apps/Makefile.dep b/dcmnet/apps/Makefile.dep index d8f4d2f1..cb3941e4 100644 --- a/dcmnet/apps/Makefile.dep +++ b/dcmnet/apps/Makefile.dep @@ -678,7 +678,12 @@ movescu.o: movescu.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcmetinf.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdicent.h \ ../../dcmdata/include/dcmtk/dcmdata/dcostrmz.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcostrma.h + ../../dcmdata/include/dcmtk/dcmdata/dcostrma.h \ + ../../dcmtls/include/dcmtk/dcmtls/tlsopt.h \ + ../../dcmtls/include/dcmtk/dcmtls/tlslayer.h \ + ../include/dcmtk/dcmnet/dcmlayer.h \ + ../../dcmtls/include/dcmtk/dcmtls/tlsdefin.h \ + ../../dcmtls/include/dcmtk/dcmtls/tlsciphr.h storescp.o: storescp.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofstd.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ diff --git a/dcmnet/apps/Makefile.in b/dcmnet/apps/Makefile.in index 4818e031..36700c15 100644 --- a/dcmnet/apps/Makefile.in +++ b/dcmnet/apps/Makefile.in @@ -57,7 +57,7 @@ findscu: findscu.o $(CXX) $(CXXFLAGS) $(LIBDIRS) $(LDFLAGS) -o $@ $@.o $(LOCALLIBS) $(DCMTLSLIBS) $(OPENSSLLIBS) $(LIBS) movescu: movescu.o - $(CXX) $(CXXFLAGS) $(LIBDIRS) $(LDFLAGS) -o $@ $@.o $(LOCALLIBS) $(LIBS) + $(CXX) $(CXXFLAGS) $(LIBDIRS) $(LDFLAGS) -o $@ $@.o $(LOCALLIBS) $(DCMTLSLIBS) $(OPENSSLLIBS) $(LIBS) termscu: termscu.o $(CXX) $(CXXFLAGS) $(LIBDIRS) $(LDFLAGS) -o $@ $@.o $(LOCALLIBS) $(LIBS) diff --git a/dcmnet/apps/dcmrecv.cc b/dcmnet/apps/dcmrecv.cc index 6838fdd6..4934a2ec 100644 --- a/dcmnet/apps/dcmrecv.cc +++ b/dcmnet/apps/dcmrecv.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2013-2024, OFFIS e.V. + * Copyright (C) 2013-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -108,6 +108,11 @@ int main(int argc, char *argv[]) cmd.addOption("--verbose-pc", "+v", "show presentation contexts in verbose mode"); cmd.addGroup("network options:"); + cmd.addSubGroup("IP protocol version:"); + cmd.addOption("--ipv4", "-i4", "use IPv4 only (default)"); + cmd.addOption("--ipv6", "-i6", "use IPv6 only"); + cmd.addOption("--ip-auto", "-i0", "use IPv6/IPv4 dual stack"); + cmd.addSubGroup("association negotiation profile from configuration file:"); cmd.addOption("--config-file", "-xf", 2, "[f]ilename, [p]rofile: string", "use profile p from configuration file f"); @@ -177,7 +182,7 @@ int main(int argc, char *argv[]) return EXITCODE_NO_ERROR; } - // check if the command line contains the --list-profiles option + /* check if the command line contains the --list-profiles option */ if (tlsOptions.listOfProfilesRequested(cmd)) { tlsOptions.printSupportedTLSProfiles(app, COUT); @@ -195,6 +200,17 @@ int main(int argc, char *argv[]) } /* network options */ + + /* set the IP protocol version */ + cmd.beginOptionBlock(); + if (cmd.findOption("--ipv4")) + dcmIncomingProtocolFamily.set(ASC_AF_INET); + if (cmd.findOption("--ipv6")) + dcmIncomingProtocolFamily.set(ASC_AF_INET6); + if (cmd.findOption("--ip-auto")) + dcmIncomingProtocolFamily.set(ASC_AF_UNSPEC); + cmd.endOptionBlock(); + if (cmd.findOption("--config-file")) { app.checkValue(cmd.getValue(opt_configFile)); diff --git a/dcmnet/apps/echoscu.cc b/dcmnet/apps/echoscu.cc index c79869b2..4bdb4251 100644 --- a/dcmnet/apps/echoscu.cc +++ b/dcmnet/apps/echoscu.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -89,6 +89,7 @@ static const char* transferSyntaxes[] = { UID_JPEGProcess14SV1TransferSyntax, UID_RLELosslessTransferSyntax, UID_DeflatedExplicitVRLittleEndianTransferSyntax, + UID_DeflatedImageFrameCompressionTransferSyntax, UID_JPEGLSLosslessTransferSyntax, UID_JPEGLSLossyTransferSyntax, UID_JPEG2000LosslessOnlyTransferSyntax, diff --git a/dcmnet/apps/movescu.cc b/dcmnet/apps/movescu.cc index bde6ec28..fe37c8d9 100644 --- a/dcmnet/apps/movescu.cc +++ b/dcmnet/apps/movescu.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -36,6 +36,7 @@ #include "dcmtk/dcmdata/dcdicent.h" #include "dcmtk/dcmdata/dcostrmz.h" /* for dcmZlibCompressionLevel */ #include "dcmtk/ofstd/ofstd.h" +#include "dcmtk/dcmtls/tlsopt.h" /* for DcmTLSOptions */ #ifdef WITH_ZLIB #include /* for zlibVersion() */ @@ -63,7 +64,7 @@ static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" #define EXITCODE_CANNOT_CLOSE_ASSOCIATION 67 #define EXITCODE_CMOVE_WARNING 68 #define EXITCODE_CMOVE_ERROR 69 - +#define EXITCODE_CANNOT_CREATE_TLS_LAYER 70 typedef enum { QMPatientRoot = 0, @@ -203,7 +204,7 @@ addOverrideKey(OFConsoleApplication& app, const char *s) } } -static OFCondition cmove(T_ASC_Association *assoc, const char *fname); +static OFCondition cmove(T_ASC_Association *assoc, const char *fname, OFBool secureConnection); static OFCondition addPresentationContext(T_ASC_Parameters *params, @@ -224,6 +225,7 @@ main(int argc, char *argv[]) const char *opt_peerTitle = PEERAPPLICATIONTITLE; const char *opt_ourTitle = APPLICATIONTITLE; OFList fileNameList; + DcmTLSOptions tlsOptions(NET_ACCEPTORREQUESTOR); OFStandard::initializeNetwork(); @@ -244,6 +246,10 @@ main(int argc, char *argv[]) OFLog::addOptions(cmd); cmd.addGroup("network options:"); + cmd.addSubGroup("IP protocol version:"); + cmd.addOption("--ipv4", "-i4", "use IPv4 only (default)"); + cmd.addOption("--ipv6", "-i6", "use IPv6 only"); + cmd.addOption("--ip-auto", "-i0", "use IPv6/IPv4 dual stack"); cmd.addSubGroup("override matching keys:"); cmd.addOption("--key", "-k", 1, "[k]ey: gggg,eeee=\"str\" or dict. name=\"str\"", "override matching key"); @@ -335,6 +341,10 @@ main(int argc, char *argv[]) cmd.addOption("--cancel", 1, "[n]umber: integer", "cancel after n responses (default: never)"); cmd.addOption("--uid-padding", "-up", "silently correct space-padded UIDs"); + + // add TLS specific command line options if (and only if) we are compiling with OpenSSL + tlsOptions.addTLSCommandlineOptions(cmd); + cmd.addGroup("output options:"); cmd.addSubGroup("general:"); cmd.addOption("--output-directory", "-od", 1, "[d]irectory: string (default: \".\")", "write received objects to existing directory d"); @@ -383,7 +393,7 @@ main(int argc, char *argv[]) { app.printHeader(OFTrue /*print host identifier*/); COUT << OFendl << "External libraries used:"; -#if !defined(WITH_ZLIB) && !defined(WITH_TCPWRAPPER) +#if !defined(WITH_ZLIB) && !defined(WITH_TCPWRAPPER) && !defined(WITH_OPENSSL) COUT << " none" << OFendl; #else COUT << OFendl; @@ -394,10 +404,26 @@ main(int argc, char *argv[]) #ifdef WITH_TCPWRAPPER COUT << "- LIBWRAP" << OFendl; #endif + // print OpenSSL version if (and only if) we are compiling with OpenSSL + tlsOptions.printLibraryVersion(); return EXITCODE_NO_ERROR; } } + // check if the command line contains the --list-ciphers option + if (tlsOptions.listOfCiphersRequested(cmd)) + { + tlsOptions.printSupportedCiphersuites(app, COUT); + return EXITCODE_NO_ERROR; + } + + // check if the command line contains the --list-profiles option + if (tlsOptions.listOfProfilesRequested(cmd)) + { + tlsOptions.printSupportedTLSProfiles(app, COUT); + return EXITCODE_NO_ERROR; + } + /* command line parameters */ cmd.getParam(1, opt_peer); @@ -470,6 +496,13 @@ main(int argc, char *argv[]) #endif cmd.endOptionBlock(); + // set the IP protocol version + cmd.beginOptionBlock(); + if (cmd.findOption("--ipv4")) dcmIncomingProtocolFamily.set(ASC_AF_INET); + if (cmd.findOption("--ipv6")) dcmIncomingProtocolFamily.set(ASC_AF_INET6); + if (cmd.findOption("--ip-auto")) dcmIncomingProtocolFamily.set(ASC_AF_UNSPEC); + cmd.endOptionBlock(); + #ifdef WITH_TCPWRAPPER cmd.beginOptionBlock(); if (cmd.findOption("--access-full")) dcmTCPWrapperDaemonName.set(NULL); @@ -517,6 +550,9 @@ main(int argc, char *argv[]) if (cmd.findOption("--cancel")) app.checkValue(cmd.getValueAndCheckMin(opt_cancelAfterNResponses, 0)); if (cmd.findOption("--uid-padding")) opt_correctUIDPadding = OFTrue; + // evaluate (most of) the TLS command line options (if we are compiling with OpenSSL) + tlsOptions.parseArguments(app, cmd); + if (cmd.findOption("--output-directory")) { app.checkDependence("--output-directory", "--port", opt_retrievePort > 0); @@ -787,9 +823,21 @@ main(int argc, char *argv[]) } ASC_setAPTitles(params, opt_ourTitle, opt_peerTitle, NULL); + // use the same network protocol family for incoming and outgoing connections + ASC_setProtocolFamily(params, dcmIncomingProtocolFamily.get()); + OFStandard::snprintf(peerHost, sizeof(peerHost), "%s:%d", opt_peer, OFstatic_cast(int, opt_port)); ASC_setPresentationAddresses(params, OFStandard::getHostName().c_str(), peerHost); + OFBool secureConnection = tlsOptions.secureConnectionRequested(); + + /* create a secure transport layer if requested and OpenSSL is available */ + cond = tlsOptions.createTransportLayer(net, params, app, cmd); + if (cond.bad()) { + OFLOG_FATAL(movescuLogger, DimseCondition::dump(temp_str, cond)); + return EXITCODE_CANNOT_CREATE_TLS_LAYER; + } + /* * We also add a presentation context for the corresponding * find sop class. @@ -838,13 +886,13 @@ main(int argc, char *argv[]) if (fileNameList.empty()) { /* no files provided on command line */ - cond = cmove(assoc, NULL); + cond = cmove(assoc, NULL, secureConnection); } else { OFListIterator(OFString) iter = fileNameList.begin(); OFListIterator(OFString) enditer = fileNameList.end(); while ((iter != enditer) && cond.good()) { - cond = cmove(assoc, (*iter).c_str()); + cond = cmove(assoc, (*iter).c_str(), secureConnection); ++iter; } } @@ -909,6 +957,12 @@ main(int argc, char *argv[]) OFStandard::shutdownNetwork(); + cond = tlsOptions.writeRandomSeed(); + if (cond.bad()) { + // failure to write back the random seed is a warning, not an error + OFLOG_WARN(movescuLogger, DimseCondition::dump(temp_str, cond)); + } + return cmove_status_code; } @@ -989,7 +1043,7 @@ addPresentationContext(T_ASC_Parameters *params, } static OFCondition -acceptSubAssoc(T_ASC_Network *aNet, T_ASC_Association **assoc) +acceptSubAssoc(T_ASC_Network *aNet, T_ASC_Association **assoc, OFBool secureConnection) { const char *knownAbstractSyntaxes[] = { UID_VerificationSOPClass @@ -997,11 +1051,11 @@ acceptSubAssoc(T_ASC_Network *aNet, T_ASC_Association **assoc) const char* transferSyntaxes[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 10 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 20 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 30 - NULL, NULL, NULL, NULL, NULL }; // +5 + NULL, NULL, NULL, NULL, NULL, NULL }; // +6 int numTransferSyntaxes; OFString temp_str; - OFCondition cond = ASC_receiveAssociation(aNet, assoc, opt_maxPDU); + OFCondition cond = ASC_receiveAssociation(aNet, assoc, opt_maxPDU, NULL, NULL, secureConnection); if (cond.good()) { OFLOG_INFO(movescuLogger, "Sub-Association Received"); @@ -1210,18 +1264,19 @@ acceptSubAssoc(T_ASC_Network *aNet, T_ASC_Association **assoc) transferSyntaxes[27] = UID_JPEGXLLosslessTransferSyntax; transferSyntaxes[28] = UID_JPEGXLJPEGRecompressionTransferSyntax; transferSyntaxes[29] = UID_JPEGXLTransferSyntax; - transferSyntaxes[30] = UID_DeflatedExplicitVRLittleEndianTransferSyntax; - transferSyntaxes[31] = UID_EncapsulatedUncompressedExplicitVRLittleEndianTransferSyntax; + transferSyntaxes[30] = UID_DeflatedImageFrameCompressionTransferSyntax; + transferSyntaxes[31] = UID_DeflatedExplicitVRLittleEndianTransferSyntax; + transferSyntaxes[32] = UID_EncapsulatedUncompressedExplicitVRLittleEndianTransferSyntax; if (gLocalByteOrder == EBO_LittleEndian) { - transferSyntaxes[32] = UID_LittleEndianExplicitTransferSyntax; - transferSyntaxes[33] = UID_BigEndianExplicitTransferSyntax; - } else { - transferSyntaxes[32] = UID_BigEndianExplicitTransferSyntax; transferSyntaxes[33] = UID_LittleEndianExplicitTransferSyntax; + transferSyntaxes[34] = UID_BigEndianExplicitTransferSyntax; + } else { + transferSyntaxes[33] = UID_BigEndianExplicitTransferSyntax; + transferSyntaxes[34] = UID_LittleEndianExplicitTransferSyntax; } - transferSyntaxes[34] = UID_LittleEndianImplicitTransferSyntax; - numTransferSyntaxes = 35; + transferSyntaxes[35] = UID_LittleEndianImplicitTransferSyntax; + numTransferSyntaxes = 36; } else { /* We prefer explicit transfer syntaxes. * If we are running on a Little Endian machine we prefer @@ -1570,15 +1625,19 @@ subOpSCP(T_ASC_Association **subAssoc) } static void -subOpCallback(void * /*subOpCallbackData*/ , +subOpCallback(void *subOpCallbackData, T_ASC_Network *aNet, T_ASC_Association **subAssoc) { - if (aNet == NULL) return; /* help no net ! */ + OFBool secureConnection = OFFalse; + if (subOpCallbackData) { + secureConnection = * OFreinterpret_cast(OFBool *, subOpCallbackData); + } + if (*subAssoc == NULL) { /* negotiate association */ - acceptSubAssoc(aNet, subAssoc); + acceptSubAssoc(aNet, subAssoc, secureConnection); } else { /* be a service class provider */ subOpSCP(subAssoc); @@ -1637,7 +1696,7 @@ substituteOverrideKeys(DcmDataset *dset) static OFCondition -moveSCU(T_ASC_Association *assoc, const char *fname) +moveSCU(T_ASC_Association *assoc, const char *fname, OFBool secureConnection) { T_ASC_PresentationContextID presId; T_DIMSE_C_MoveRQ req; @@ -1692,7 +1751,7 @@ moveSCU(T_ASC_Association *assoc, const char *fname) OFCondition cond = DIMSE_moveUser(assoc, presId, &req, dcmff.getDataset(), moveCallback, &callbackData, opt_blockMode, opt_dimse_timeout, net, subOpCallback, - NULL, &rsp, &statusDetail, &rspIds, opt_ignorePendingDatasets); + &secureConnection, &rsp, &statusDetail, &rspIds, opt_ignorePendingDatasets); if (cond == EC_Normal) { @@ -1739,11 +1798,11 @@ moveSCU(T_ASC_Association *assoc, const char *fname) static OFCondition -cmove(T_ASC_Association *assoc, const char *fname) +cmove(T_ASC_Association *assoc, const char *fname, OFBool secureConnection) { OFCondition cond = EC_Normal; int n = OFstatic_cast(int, opt_repeatCount); while (cond.good() && n--) - cond = moveSCU(assoc, fname); + cond = moveSCU(assoc, fname, secureConnection); return cond; } diff --git a/dcmnet/apps/storescp.cc b/dcmnet/apps/storescp.cc index 8969c92e..7a69a161 100644 --- a/dcmnet/apps/storescp.cc +++ b/dcmnet/apps/storescp.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -22,12 +22,8 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ BEGIN_EXTERN_C -#ifdef HAVE_SYS_STAT_H #include -#endif -#ifdef HAVE_FCNTL_H #include /* needed on Solaris for O_RDONLY */ -#endif // On Solaris with Sun Workshop 11, declares signal() but does not #include @@ -42,6 +38,7 @@ END_EXTERN_C #include "dcmtk/ofstd/ofstd.h" #include "dcmtk/ofstd/ofconapp.h" #include "dcmtk/ofstd/ofbmanip.h" /* for OFBitmanipTemplate */ +#include "dcmtk/ofstd/oflist.h" #include "dcmtk/ofstd/ofdatime.h" #include "dcmtk/dcmnet/dicom.h" /* for DICOM_APPLICATION_ACCEPTOR */ #include "dcmtk/dcmnet/dimse.h" @@ -114,6 +111,59 @@ enum E_SortStudyMode ESM_PatientName }; +#ifdef WIN32 +struct ChildProcessData +{ + HANDLE processHandle; + HANDLE waitHandle; + bool done; +}; + +OFList< ChildProcessData * > ChildProcessList; +HANDLE ChildProcessEvent = NULL; + +// This callback function will be executed by Windows in a separate thread when +// a child process has ended, similar to the SIGCHLD callback on Posix systems +static void CALLBACK onExitedCallback(void* context, BOOLEAN /* isTimeOut */) +{ + // mark the related entry for the client process that has ended as "done", i.e. ready for clean-up + bool *done = OFstatic_cast(bool *, context); + *done = true; + + // if the main thread is in a blocking wait in cleanChildren() because + // the maximum number of child processes was running, let the main thread continue now + if (ChildProcessEvent) SetEvent(ChildProcessEvent); +} + +// This callback function will be called in the main thread by receiveTransportConnectionTCP() +// whenever a new child process has been create by CreateProcessA(). +// The context parameter is a pointer to the process handle, which we must store +// and close when not needed anymore. +static void processCreatedCallback(void *context) +{ + // event handler for blocking wait in cleanChildren() + if (ChildProcessEvent == NULL) ChildProcessEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + + // create new entry in list of child processes + ChildProcessData *cbd = new ChildProcessData(); + cbd->processHandle = OFstatic_cast(HANDLE, context); + cbd->done = false; + + // request Windows to call onExitedCallback() when the child process with the given process handle has exited + if (RegisterWaitForSingleObject(&cbd->waitHandle, cbd->processHandle, onExitedCallback, &cbd->done, INFINITE, WT_EXECUTEONLYONCE)) + { + ChildProcessList.push_back(cbd); + } + else + { + OFLOG_WARN(storescpLogger, "RegisterWaitForSingleObject() failed, unable to track child process"); + CloseHandle(cbd->processHandle); + delete cbd; + } +} + +#endif + OFBool opt_showPresentationContexts = OFFalse; OFBool opt_uniqueFilenames = OFFalse; OFString opt_fileNameExtension; @@ -172,20 +222,28 @@ OFBool opt_forkMode = OFFalse; OFBool opt_forkedChild = OFFalse; OFBool opt_execSync = OFFalse; // default: execute in background - +volatile size_t numChildren = 0; +OFCmdUnsignedInt opt_maxChildren = OFstatic_cast(OFCmdUnsignedInt, -1); #ifdef HAVE_WAITPID + /** signal handler for SIGCHLD signals that immediately cleans up - * terminated children. + * terminated children and adjusts the count of child processes */ extern "C" void sigChildHandler(int) { - int status = 0; - waitpid( -1, &status, WNOHANG ); - signal(SIGCHLD, sigChildHandler); + while (waitpid( -1, NULL, WNOHANG) > 0) + { + if (numChildren > 0) + { + // In C++20, operator-- on variables with volateile qualifier is deprecated. + size_t n = numChildren - 1; + numChildren = n; + } + } } -#endif +#endif /* helper macro for converting stream output to a string */ #define CONVERT_TO_STRING(output, string) \ @@ -231,9 +289,14 @@ int main(int argc, char *argv[]) #ifdef _WIN32 cmd.addOption("--forked-child", "process is forked child, internal use only", OFCommandLine::AF_Internal); #endif + cmd.addOption("--max-associations", 1, "[m]ax: integer (default: unlimited)", "limit number of parallel associations to m"); #endif cmd.addGroup("network options:"); + cmd.addSubGroup("IP protocol version:"); + cmd.addOption("--ipv4", "-i4", "use IPv4 only (default)"); + cmd.addOption("--ipv6", "-i6", "use IPv6 only"); + cmd.addOption("--ip-auto", "-i0", "use IPv6/IPv4 dual stack"); cmd.addSubGroup("association negotiation profile from configuration file:"); cmd.addOption("--config-file", "-xf", 2, "[f]ilename, [p]rofile: string", "use profile p from config file f"); @@ -415,6 +478,14 @@ int main(int argc, char *argv[]) { opt_inetd_mode = OFTrue; opt_forkMode = OFFalse; + if (cmd.findOption("--fork")) + { + app.checkConflict("--inetd", "--fork", opt_inetd_mode); + } + if (cmd.findOption("--max-associations")) + { + app.checkDependence("--max-associations", "--fork", opt_forkMode); + } // duplicate stdin, which is the socket passed by inetd int inetd_fd = dup(0); @@ -450,13 +521,17 @@ int main(int argc, char *argv[]) opt_forkMode = OFFalse; if (cmd.findOption("--fork")) { - app.checkConflict("--inetd", "--fork", opt_inetd_mode); opt_forkMode = OFTrue; } cmd.endOptionBlock(); #ifdef _WIN32 if (cmd.findOption("--forked-child")) opt_forkedChild = OFTrue; #endif + if (cmd.findOption("--max-associations")) + { + app.checkDependence("--max-associations", "--fork", opt_forkMode); + app.checkValue(cmd.getValueAndCheckMin(opt_maxChildren, 1)); + } #endif if (opt_inetd_mode) @@ -549,6 +624,13 @@ int main(int argc, char *argv[]) if (cmd.findOption("--promiscuous")) opt_promiscuous = OFTrue; if (cmd.findOption("--uid-padding")) opt_correctUIDPadding = OFTrue; + // set the IP protocol version + cmd.beginOptionBlock(); + if (cmd.findOption("--ipv4")) dcmIncomingProtocolFamily.set(ASC_AF_INET); + if (cmd.findOption("--ipv6")) dcmIncomingProtocolFamily.set(ASC_AF_INET6); + if (cmd.findOption("--ip-auto")) dcmIncomingProtocolFamily.set(ASC_AF_UNSPEC); + cmd.endOptionBlock(); + if (cmd.findOption("--config-file")) { // check conflicts with other command line options @@ -925,7 +1007,7 @@ int main(int argc, char *argv[]) else { // parent process - if (opt_forkMode) DUL_requestForkOnTransportConnectionReceipt(argc, argv); + if (opt_forkMode) DUL_requestForkOnTransportConnectionReceipt(argc, argv, &processCreatedCallback); } #endif @@ -953,16 +1035,35 @@ int main(int argc, char *argv[]) #ifdef HAVE_WAITPID // register signal handler - signal(SIGCHLD, sigChildHandler); + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; + sa.sa_handler = sigChildHandler; + sigaction(SIGCHLD, &sa, NULL); #endif while (cond.good()) { +#if defined(HAVE_WAITPID) || defined(WIN32) + /* if the maximum number of child processes is active, + * wait until at least one child terminates before + * continuing to accept incoming associations + */ + if (numChildren == opt_maxChildren) + { + OFLOG_INFO(storescpLogger, "Maximum number of associations reached, waiting for child process to terminate"); + while (numChildren == opt_maxChildren) + { + cleanChildren(-1, OFTrue); + } + } +#endif + /* receive an association and acknowledge or reject it. If the association was */ /* acknowledged, offer corresponding services and invoke one or more if required. */ cond = acceptAssociation(net, asccfg, tlsOptions.secureConnectionRequested()); - /* remove zombie child processes */ + /* remove zombie child processes, do not block */ cleanChildren(-1, OFFalse); /* since storescp is usually terminated with SIGTERM or the like, @@ -1015,7 +1116,7 @@ static OFCondition acceptAssociation(T_ASC_Network *net, DcmAssociationConfigura const char* transferSyntaxes[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 10 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 20 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 30 - NULL, NULL, NULL, NULL, NULL }; // +5 + NULL, NULL, NULL, NULL, NULL, NULL }; // +6 int numTransferSyntaxes = 0; // try to receive an association. Here we either want to use blocking or @@ -1027,7 +1128,11 @@ static OFCondition acceptAssociation(T_ASC_Network *net, DcmAssociationConfigura if (cond.code() == DULC_FORKEDCHILD) { - // OFLOG_DEBUG(storescpLogger, DimseCondition::dump(temp_str, cond)); + // we are the parent process in fork mode and have successfully forked a child process + // that will handle the association. Just clean up the association in this process. + // Note: in C++20, operator++ on variables with volateile qualifier is deprecated. + size_t n = numChildren + 1; + numChildren = n; goto cleanup; } @@ -1080,8 +1185,10 @@ static OFCondition acceptAssociation(T_ASC_Network *net, DcmAssociationConfigura #if defined(HAVE_FORK) || defined(_WIN32) if (opt_forkMode) + { OFLOG_INFO(storescpLogger, "Association Received in " << (DUL_processIsForkedChild() ? "child" : "parent") << " process (pid: " << OFStandard::getProcessID() << ")"); + } else #endif OFLOG_INFO(storescpLogger, "Association Received"); @@ -1313,18 +1420,19 @@ static OFCondition acceptAssociation(T_ASC_Network *net, DcmAssociationConfigura transferSyntaxes[27] = UID_JPEGXLLosslessTransferSyntax; transferSyntaxes[28] = UID_JPEGXLJPEGRecompressionTransferSyntax; transferSyntaxes[29] = UID_JPEGXLTransferSyntax; - transferSyntaxes[30] = UID_DeflatedExplicitVRLittleEndianTransferSyntax; - transferSyntaxes[31] = UID_EncapsulatedUncompressedExplicitVRLittleEndianTransferSyntax; + transferSyntaxes[30] = UID_DeflatedImageFrameCompressionTransferSyntax; + transferSyntaxes[31] = UID_DeflatedExplicitVRLittleEndianTransferSyntax; + transferSyntaxes[32] = UID_EncapsulatedUncompressedExplicitVRLittleEndianTransferSyntax; if (gLocalByteOrder == EBO_LittleEndian) { - transferSyntaxes[32] = UID_LittleEndianExplicitTransferSyntax; - transferSyntaxes[33] = UID_BigEndianExplicitTransferSyntax; - } else { - transferSyntaxes[32] = UID_BigEndianExplicitTransferSyntax; transferSyntaxes[33] = UID_LittleEndianExplicitTransferSyntax; + transferSyntaxes[34] = UID_BigEndianExplicitTransferSyntax; + } else { + transferSyntaxes[33] = UID_BigEndianExplicitTransferSyntax; + transferSyntaxes[34] = UID_LittleEndianExplicitTransferSyntax; } - transferSyntaxes[34] = UID_LittleEndianImplicitTransferSyntax; - numTransferSyntaxes = 35; + transferSyntaxes[35] = UID_LittleEndianImplicitTransferSyntax; + numTransferSyntaxes = 36; } else { /* We prefer explicit transfer syntaxes. * If we are running on a Little Endian machine we prefer @@ -2457,9 +2565,12 @@ static void executeCommand( const OFString &cmd ) OFLOG_ERROR(storescpLogger, "cannot execute command '" << cmd << "' (fork failed)"); else if (pid > 0) { - /* we are the parent process */ - /* remove pending zombie child processes */ - cleanChildren(pid, opt_execSync); + /* we are the parent process. If we are in fork mode or + * in synchronous exec mode, wait for the child process to terminate + * and then clean up the process to avoid interference with the + * counter that counts the number of child process in the main process + */ + cleanChildren(pid, opt_execSync || opt_forkMode); } else // in case we are the child process, execute the command etc. { @@ -2501,7 +2612,7 @@ static void executeCommand( const OFString &cmd ) #ifdef HAVE_WAITPID static void cleanChildren(pid_t pid, OFBool synch) #else -static void cleanChildren(pid_t /* pid */, OFBool /* synch */) +static void cleanChildren(pid_t /* pid */, OFBool synch) #endif /* * This function removes child processes that have terminated, @@ -2509,23 +2620,57 @@ static void cleanChildren(pid_t /* pid */, OFBool /* synch */) */ { #ifdef HAVE_WAITPID - int stat_loc; - int child = 1; - int options = synch ? 0 : WNOHANG; - while (child > 0) - { - child = OFstatic_cast(int, waitpid(pid, &stat_loc, options)); - if (child < 0) + int stat_loc; + int child = 1; + int options = synch ? 0 : WNOHANG; + while (child > 0) { - if (errno != ECHILD) + child = OFstatic_cast(int, waitpid(pid, &stat_loc, options)); + if (child > 0) { - char buf[256]; - OFLOG_WARN(storescpLogger, "wait for child failed: " << OFStandard::strerror(errno, buf, sizeof(buf))); + if (numChildren > 0) + { + // In C++20, operator-- on variables with volateile qualifier is deprecated. + size_t n = numChildren - 1; + numChildren = n; + } + } + else if (child < 0) + { + if (errno != ECHILD) + { + char buf[256]; + OFLOG_WARN(storescpLogger, "wait for child failed: " << OFStandard::strerror(errno, buf, sizeof(buf))); + } } + if (synch) options = 0; // break out of loop + } +#elif defined(WIN32) + if (synch && ChildProcessEvent) + { + // block until a child process has ended + WaitForSingleObject(ChildProcessEvent, INFINITE); + } + OFListIterator(ChildProcessData *) first = ChildProcessList.begin(); + OFListIterator(ChildProcessData *) last = ChildProcessList.end(); + while (first != last) + { + if ((*first)->done) + { + // clean up wait handle + UnregisterWaitEx((*first)->waitHandle, INVALID_HANDLE_VALUE); + // close process handle + CloseHandle((*first)->processHandle); + // delete list entry + delete *first; + first = ChildProcessList.erase(first); + // decrease counter for child processes + // Note: in C++20, operator-- on variables with volateile qualifier is deprecated. + size_t n = numChildren - 1; + numChildren = n; + } + else ++first; } - - if (synch) child = -1; // break out of loop - } #endif } diff --git a/dcmnet/docs/dcmrecv.man b/dcmnet/docs/dcmrecv.man index 037553b4..9be99c86 100644 --- a/dcmnet/docs/dcmrecv.man +++ b/dcmnet/docs/dcmrecv.man @@ -63,6 +63,17 @@ port tcp/ip port number to listen on \subsection dcmrecv_network_options network options \verbatim +IP protocol version: + + -i4 --ipv4 + use IPv4 only (default) + + -i6 --ipv6 + use IPv6 only + + -i0 --ip-auto + use IPv6/IPv4 dual stack + association negotiation profile from configuration file: -xf --config-file [f]ilename, [p]rofile: string @@ -480,6 +491,6 @@ It is an error if no data dictionary can be loaded. \section dcmrecv_copyright COPYRIGHT -Copyright (C) 2013-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2013-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmnet/docs/dcmsend.man b/dcmnet/docs/dcmsend.man index b98db6ef..72a599ac 100644 --- a/dcmnet/docs/dcmsend.man +++ b/dcmnet/docs/dcmsend.man @@ -420,6 +420,6 @@ It is an error if no data dictionary can be loaded. \section dcmsend_copyright COPYRIGHT -Copyright (C) 2011-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2011-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmnet/docs/echoscu.man b/dcmnet/docs/echoscu.man index 0886cd1b..9a12a9de 100644 --- a/dcmnet/docs/echoscu.man +++ b/dcmnet/docs/echoscu.man @@ -80,7 +80,7 @@ application entity titles: association negotiation debugging: - -pts --propose-ts [n]umber: integer (1..52) + -pts --propose-ts [n]umber: integer (1..53) propose n transfer syntaxes -ppc --propose-pc [n]umber: integer (1..128) @@ -405,6 +405,6 @@ It is an error if no data dictionary can be loaded. \section echoscu_copyright COPYRIGHT -Copyright (C) 1994-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1994-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmnet/docs/findscu.man b/dcmnet/docs/findscu.man index 321df261..4c921cd4 100644 --- a/dcmnet/docs/findscu.man +++ b/dcmnet/docs/findscu.man @@ -508,6 +508,6 @@ replace any built-in tables. \section findscu_copyright COPYRIGHT -Copyright (C) 1994-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1994-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmnet/docs/getscu.man b/dcmnet/docs/getscu.man index c7ef7ae5..f5d178c1 100644 --- a/dcmnet/docs/getscu.man +++ b/dcmnet/docs/getscu.man @@ -476,6 +476,8 @@ BasicVoiceAudioWaveformStorage 1.2.840.10008.5.1.4.1.1.9.4 GeneralAudioWaveformStorage 1.2.840.10008.5.1.4.1.1.9.4.2 ArterialPulseWaveformStorage 1.2.840.10008.5.1.4.1.1.9.5.1 RespiratoryWaveformStorage 1.2.840.10008.5.1.4.1.1.9.6.1 +WaveformPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.1 +WaveformAcquisitionPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.2 RETIRED_StandaloneModalityLUTStorage 1.2.840.10008.5.1.4.1.1.10 RETIRED_StandaloneVOILUTStorage 1.2.840.10008.5.1.4.1.1.11 GrayscaleSoftcopyPresentationStateStorage 1.2.840.10008.5.1.4.1.1.11.1 @@ -683,6 +685,6 @@ It is an error if no data dictionary can be loaded. \section getscu_copyright COPYRIGHT -Copyright (C) 2011-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2011-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmnet/docs/movescu.man b/dcmnet/docs/movescu.man index 7ff1be4d..643b2087 100644 --- a/dcmnet/docs/movescu.man +++ b/dcmnet/docs/movescu.man @@ -67,6 +67,17 @@ dcmfile-in DICOM query file(s) \subsection movescu_network_options network options \verbatim +IP protocol version: + + -i4 --ipv4 + use IPv4 only (default) + + -i6 --ipv6 + use IPv6 only + + -i0 --ip-auto + use IPv6/IPv4 dual stack + override matching keys: -k --key [k]ey: gggg,eeee="str" or dictionary name="str" @@ -241,6 +252,130 @@ other network options: silently correct space-padded UIDs \endverbatim +\subsection movescu_tls_options transport layer security (TLS) options +\verbatim +transport protocol stack: + + -tls --disable-tls + use normal TCP/IP connection (default) + + +tls --enable-tls [p]rivate key file, [c]ertificate file: string + use authenticated secure TLS connection + +private key password (only with --enable-tls): + + +ps --std-passwd + prompt user to type password on stdin (default) + + +pw --use-passwd [p]assword: string + use specified password + + -pw --null-passwd + use empty string as password + +key and certificate file format: + + -pem --pem-keys + read keys and certificates as PEM file (default) + + -der --der-keys + read keys and certificates as DER file + +certification authority: + + +cf --add-cert-file [f]ilename: string + add certificate file to list of certificates + + +cd --add-cert-dir [d]irectory: string + add certificates in d to list of certificates + + +crl --add-crl-file [f]ilename: string + add certificate revocation list file + (implies --enable-crl-vfy) + + +crv --enable-crl-vfy + enable leaf CRL verification + + +cra --enable-crl-all + enable full chain CRL verification + +security profile: + + +ph --list-profiles + list supported TLS profiles and exit + + +pg --profile-8996 + BCP 195 RFC 8996 TLS Profile (default) + + +pm --profile-8996-mod + Modified BCP 195 RFC 8996 TLS Profile + + # only available if underlying TLS library supports + # all TLS features required for this profile + + +py --profile-bcp195-nd + Non-downgrading BCP 195 TLS Profile (retired) + + +px --profile-bcp195 + BCP 195 TLS Profile (retired) + + +pz --profile-bcp195-ex + Extended BCP 195 TLS Profile (retired) + + +pb --profile-basic + Basic TLS Secure Transport Connection Profile (retired) + + # only available if underlying TLS library supports 3DES + + +pa --profile-aes + AES TLS Secure Transport Connection Profile (retired) + + +pn --profile-null + Authenticated unencrypted communication + (retired, was used in IHE ATNA) + +ciphersuite: + + +cc --list-ciphers + list supported TLS ciphersuites and exit + + +cs --cipher [c]iphersuite name: string + add ciphersuite to list of negotiated suites + + +dp --dhparam [f]ilename: string + read DH parameters for DH/DSS ciphersuites + +server name indication: + + --no-sni + do not use SNI (default) + + --expect-sni [s]erver name: string + expect requests for server name s + +pseudo random generator: + + +rs --seed [f]ilename: string + seed random generator with contents of f + + +ws --write-seed + write back modified seed (only with --seed) + + +wf --write-seed-file [f]ilename: string (only with --seed) + write modified seed to file f + +peer authentication: + + -rc --require-peer-cert + verify peer certificate, fail if absent (default) + + -vc --verify-peer-cert + verify peer certificate if present + + -ic --ignore-peer-cert + don't verify peer certificate +\endverbatim + \subsection movescu_output_options output options \verbatim general: @@ -480,6 +615,8 @@ ElectromyogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7 ElectrooculogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7.3 SleepElectroencephalogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7.4 BodyPositionWaveformStorage 1.2.840.10008.5.1.4.1.1.9.8.1 +WaveformPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.1 +WaveformAcquisitionPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.2 RETIRED_StandaloneModalityLUTStorage 1.2.840.10008.5.1.4.1.1.10 RETIRED_StandaloneVOILUTStorage 1.2.840.10008.5.1.4.1.1.11 GrayscaleSoftcopyPresentationStateStorage 1.2.840.10008.5.1.4.1.1.11.1 @@ -627,6 +764,7 @@ DICONDE_EddyCurrentImageStorage 1.2.840.10008.5.1.4.1.1.601 DICONDE_EddyCurrentMultiframeImageStorage 1.2.840.10008.5.1.4.1.1.601.2 DICONDE_ThermographyImageStorage 1.2.840.10008.5.1.4.1.1.601.3 DICONDE_ThermographyMultiFrameImageStorage 1.2.840.10008.5.1.4.1.1.601.4 +DICONDE_UltrasoundWaveformStorage 1.2.840.10008.5.1.4.1.1.601.5 DRAFT_RTBeamsDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.1 RTBeamsDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.7 RTBrachyApplicationSetupDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.10 @@ -685,6 +823,7 @@ HighThroughputJPEG2000ImageCompressionLossless.Tr.S. 1.2.840.10008.1.2.4.201 HighThroughputJPEG2000RPCLImageCompressionLoss.Tr.S. 1.2.840.10008.1.2.4.202 HighThroughputJPEG2000ImageCompressionTransferSynta. 1.2.840.10008.1.2.4.203 RLELosslessTransferSyntax 1.2.840.10008.1.2.5 +DeflatedImageFrameCompressionTransferSyntax 1.2.840.10008.1.2.8.1 \endverbatim (*) if compiled with zlib support enabled (see \e --version output) @@ -789,6 +928,7 @@ EXITCODE_NO_PRESENTATION_CONTEXT 66 EXITCODE_CANNOT_CLOSE_ASSOCIATION 67 EXITCODE_CMOVE_WARNING 68 EXITCODE_CMOVE_ERROR 69 +EXITCODE_CANNOT_CREATE_TLS_LAYER 70 \endverbatim \section movescu_environment ENVIRONMENT @@ -813,6 +953,6 @@ It is an error if no data dictionary can be loaded. \section movescu_copyright COPYRIGHT -Copyright (C) 1994-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1994-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmnet/docs/storescp.man b/dcmnet/docs/storescp.man index fb09d07d..ecd4113b 100644 --- a/dcmnet/docs/storescp.man +++ b/dcmnet/docs/storescp.man @@ -67,10 +67,24 @@ port tcp/ip port number to listen on --fork fork child process for each association + + --max-associations [m]ax: integer (default: unlimited) + limit number of parallel associations to m \endverbatim \subsection storescp_network_options network options \verbatim +IP protocol version: + + -i4 --ipv4 + use IPv4 only (default) + + -i6 --ipv6 + use IPv6 only + + -i0 --ip-auto + use IPv6/IPv4 dual stack + association negotiation profile from configuration file: -xf --config-file [f]ilename [p]rofile: string @@ -685,6 +699,8 @@ ElectromyogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7 ElectrooculogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7.3 SleepElectroencephalogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7.4 BodyPositionWaveformStorage 1.2.840.10008.5.1.4.1.1.9.8.1 +WaveformPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.1 +WaveformAcquisitionPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.2 RETIRED_StandaloneModalityLUTStorage 1.2.840.10008.5.1.4.1.1.10 RETIRED_StandaloneVOILUTStorage 1.2.840.10008.5.1.4.1.1.11 GrayscaleSoftcopyPresentationStateStorage 1.2.840.10008.5.1.4.1.1.11.1 @@ -832,6 +848,7 @@ DICONDE_EddyCurrentImageStorage 1.2.840.10008.5.1.4.1.1.601 DICONDE_EddyCurrentMultiframeImageStorage 1.2.840.10008.5.1.4.1.1.601.2 DICONDE_ThermographyImageStorage 1.2.840.10008.5.1.4.1.1.601.3 DICONDE_ThermographyMultiFrameImageStorage 1.2.840.10008.5.1.4.1.1.601.4 +DICONDE_UltrasoundWaveformStorage 1.2.840.10008.5.1.4.1.1.601.5 DRAFT_RTBeamsDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.1 RTBeamsDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.7 RTBrachyApplicationSetupDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.10 @@ -890,6 +907,7 @@ HighThroughputJPEG2000ImageCompressionLossless.Tr.S. 1.2.840.10008.1.2.4.201 HighThroughputJPEG2000RPCLImageCompressionLoss.Tr.S. 1.2.840.10008.1.2.4.202 HighThroughputJPEG2000ImageCompressionTransferSynta. 1.2.840.10008.1.2.4.203 RLELosslessTransferSyntax 1.2.840.10008.1.2.5 +DeflatedImageFrameCompressionTransferSyntax 1.2.840.10008.1.2.8.1 \endverbatim (*) if compiled with zlib support enabled (see \e --version output) @@ -1013,6 +1031,6 @@ It is an error if no data dictionary can be loaded. \section storescp_copyright COPYRIGHT -Copyright (C) 1996-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1996-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmnet/docs/storescu.man b/dcmnet/docs/storescu.man index 7f0fbefe..f12a47f2 100644 --- a/dcmnet/docs/storescu.man +++ b/dcmnet/docs/storescu.man @@ -638,6 +638,6 @@ It is an error if no data dictionary can be loaded. \section storescu_copyright COPYRIGHT -Copyright (C) 1996-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1996-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmnet/docs/termscu.man b/dcmnet/docs/termscu.man index 09a16a6d..a7f76f83 100644 --- a/dcmnet/docs/termscu.man +++ b/dcmnet/docs/termscu.man @@ -154,6 +154,6 @@ It is an error if no data dictionary can be loaded. \section termscu_copyright COPYRIGHT -Copyright (C) 2005-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2005-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmnet/etc/storescp.cfg b/dcmnet/etc/storescp.cfg index 7265661f..c2584b49 100644 --- a/dcmnet/etc/storescp.cfg +++ b/dcmnet/etc/storescp.cfg @@ -1,5 +1,5 @@ # -# Copyright (C) 2003-2024, OFFIS e.V. +# Copyright (C) 2003-2025, OFFIS e.V. # All rights reserved. See COPYRIGHT file for details. # # This software and supporting documentation were developed by @@ -44,6 +44,9 @@ TransferSyntax1 = DeflatedLittleEndianExplicit TransferSyntax2 = LocalEndianExplicit TransferSyntax3 = OppositeEndianExplicit TransferSyntax4 = LittleEndianImplicit +# +# The "Deflated Image Frame Compression" transfer syntax is for images. +# [UncompressedEncapsulatedOrZlib] TransferSyntax1 = DeflatedLittleEndianExplicit @@ -53,6 +56,8 @@ TransferSyntax4 = LittleEndianImplicit # # The retired "Big Endian Explicit" transfer syntax is not accepted. # +# The "Deflated Image Frame Compression" transfer syntax is for images. +# [AnyTransferSyntax] TransferSyntax1 = JPEGBaseline @@ -102,11 +107,12 @@ TransferSyntax44 = HighThroughputJPEG2000ImageCompression TransferSyntax45 = JPEGXLLossless TransferSyntax46 = JPEGXLJPEGRecompression TransferSyntax47 = JPEGXL -TransferSyntax48 = DeflatedLittleEndianExplicit -TransferSyntax49 = EncapsulatedUncompressedLittleEndianExplicit -TransferSyntax50 = LocalEndianExplicit -TransferSyntax51 = OppositeEndianExplicit -TransferSyntax52 = LittleEndianImplicit +TransferSyntax48 = DeflatedImageFrameCompression +TransferSyntax49 = DeflatedLittleEndianExplicit +TransferSyntax50 = EncapsulatedUncompressedLittleEndianExplicit +TransferSyntax51 = LocalEndianExplicit +TransferSyntax52 = OppositeEndianExplicit +TransferSyntax53 = LittleEndianImplicit # ============================================================================ [[PresentationContexts]] @@ -309,46 +315,49 @@ PresentationContext178 = TwelveLeadECGWaveformStorage\UncompressedOrZlib PresentationContext179 = VariableModalityLUTSoftcopyPresentationStateStorage\UncompressedOrZlib PresentationContext180 = VisualAcuityMeasurementsStorage\UncompressedOrZlib PresentationContext181 = VolumeRenderingVolumetricPresentationStateStorage\UncompressedOrZlib -PresentationContext182 = WaveformAnnotationSRStorage\UncompressedOrZlib -PresentationContext183 = XADefinedProcedureProtocolStorage\UncompressedOrZlib -PresentationContext184 = XAPerformedProcedureProtocolStorage\UncompressedOrZlib -PresentationContext185 = XAXRFGrayscaleSoftcopyPresentationStateStorage\UncompressedOrZlib -PresentationContext186 = XRayRadiationDoseSRStorage\UncompressedOrZlib +PresentationContext182 = WaveformAcquisitionPresentationStateStorage\UncompressedOrZlib +PresentationContext183 = WaveformAnnotationSRStorage\UncompressedOrZlib +PresentationContext184 = WaveformPresentationStateStorage\UncompressedOrZlib +PresentationContext185 = XADefinedProcedureProtocolStorage\UncompressedOrZlib +PresentationContext186 = XAPerformedProcedureProtocolStorage\UncompressedOrZlib +PresentationContext187 = XAXRFGrayscaleSoftcopyPresentationStateStorage\UncompressedOrZlib +PresentationContext188 = XRayRadiationDoseSRStorage\UncompressedOrZlib # # retired non-image SOP classes # -PresentationContext187 = RETIRED_StandaloneCurveStorage\UncompressedOrZlib -PresentationContext188 = RETIRED_StandaloneModalityLUTStorage\UncompressedOrZlib -PresentationContext189 = RETIRED_StandaloneOverlayStorage\UncompressedOrZlib -PresentationContext190 = RETIRED_StandalonePETCurveStorage\UncompressedOrZlib -PresentationContext191 = RETIRED_StandaloneVOILUTStorage\UncompressedOrZlib -PresentationContext192 = RETIRED_StoredPrintStorage\UncompressedOrZlib +PresentationContext189 = RETIRED_StandaloneCurveStorage\UncompressedOrZlib +PresentationContext190 = RETIRED_StandaloneModalityLUTStorage\UncompressedOrZlib +PresentationContext191 = RETIRED_StandaloneOverlayStorage\UncompressedOrZlib +PresentationContext192 = RETIRED_StandalonePETCurveStorage\UncompressedOrZlib +PresentationContext193 = RETIRED_StandaloneVOILUTStorage\UncompressedOrZlib +PresentationContext194 = RETIRED_StoredPrintStorage\UncompressedOrZlib # # draft non-image SOP classes # -PresentationContext193 = DRAFT_RTBeamsDeliveryInstructionStorage\UncompressedOrZlib -PresentationContext194 = DRAFT_SRAudioStorage\UncompressedOrZlib -PresentationContext195 = DRAFT_SRComprehensiveStorage\UncompressedOrZlib -PresentationContext196 = DRAFT_SRDetailStorage\UncompressedOrZlib -PresentationContext197 = DRAFT_SRTextStorage\UncompressedOrZlib -PresentationContext198 = DRAFT_WaveformStorage\UncompressedOrZlib +PresentationContext195 = DRAFT_RTBeamsDeliveryInstructionStorage\UncompressedOrZlib +PresentationContext196 = DRAFT_SRAudioStorage\UncompressedOrZlib +PresentationContext197 = DRAFT_SRComprehensiveStorage\UncompressedOrZlib +PresentationContext198 = DRAFT_SRDetailStorage\UncompressedOrZlib +PresentationContext199 = DRAFT_SRTextStorage\UncompressedOrZlib +PresentationContext200 = DRAFT_WaveformStorage\UncompressedOrZlib # # DICOS Storage # -PresentationContext199 = DICOS_CTImageStorage\AnyTransferSyntax -PresentationContext200 = DICOS_DigitalXRayImageStorageForPresentation\AnyTransferSyntax -PresentationContext201 = DICOS_DigitalXRayImageStorageForProcessing\AnyTransferSyntax -PresentationContext202 = DICOS_2DAITStorage\AnyTransferSyntax -PresentationContext203 = DICOS_3DAITStorage\AnyTransferSyntax -PresentationContext204 = DICOS_QuadrupoleResonanceStorage\UncompressedOrZlib -PresentationContext205 = DICOS_ThreatDetectionReportStorage\UncompressedOrZlib +PresentationContext201 = DICOS_CTImageStorage\AnyTransferSyntax +PresentationContext202 = DICOS_DigitalXRayImageStorageForPresentation\AnyTransferSyntax +PresentationContext203 = DICOS_DigitalXRayImageStorageForProcessing\AnyTransferSyntax +PresentationContext204 = DICOS_2DAITStorage\AnyTransferSyntax +PresentationContext205 = DICOS_3DAITStorage\AnyTransferSyntax +PresentationContext206 = DICOS_QuadrupoleResonanceStorage\UncompressedOrZlib +PresentationContext207 = DICOS_ThreatDetectionReportStorage\UncompressedOrZlib # # DICONDE Storage # -PresentationContext206 = DICONDE_EddyCurrentImageStorage\AnyTransferSyntax -PresentationContext207 = DICONDE_EddyCurrentMultiframeImageStorage\AnyTransferSyntax -PresentationContext208 = DICONDE_ThermographyImageStorage\AnyTransferSyntax -PresentationContext209 = DICONDE_ThermographyMultiFrameImageStorage\AnyTransferSyntax +PresentationContext208 = DICONDE_EddyCurrentImageStorage\AnyTransferSyntax +PresentationContext209 = DICONDE_EddyCurrentMultiframeImageStorage\AnyTransferSyntax +PresentationContext210 = DICONDE_ThermographyImageStorage\AnyTransferSyntax +PresentationContext211 = DICONDE_ThermographyMultiFrameImageStorage\AnyTransferSyntax +PresentationContext212 = DICONDE_UltrasoundWaveformStorage\UncompressedOrZlib # ---------------------------------------------------------------------------- @@ -542,11 +551,13 @@ PresentationContext170 = TwelveLeadECGWaveformStorage\UncompressedOrZlib PresentationContext171 = VariableModalityLUTSoftcopyPresentationStateStorage\UncompressedOrZlib PresentationContext172 = VisualAcuityMeasurementsStorage\UncompressedOrZlib PresentationContext173 = VolumeRenderingVolumetricPresentationStateStorage\UncompressedOrZlib -PresentationContext174 = WaveformAnnotationSRStorage\UncompressedOrZlib -PresentationContext175 = XADefinedProcedureProtocolStorage\UncompressedOrZlib -PresentationContext176 = XAPerformedProcedureProtocolStorage\UncompressedOrZlib -PresentationContext177 = XAXRFGrayscaleSoftcopyPresentationStateStorage\UncompressedOrZlib -PresentationContext178 = XRayRadiationDoseSRStorage\UncompressedOrZlib +PresentationContext174 = WaveformAcquisitionPresentationStateStorage\UncompressedOrZlib +PresentationContext175 = WaveformAnnotationSRStorage\UncompressedOrZlib +PresentationContext176 = WaveformPresentationStateStorage\UncompressedOrZlib +PresentationContext177 = XADefinedProcedureProtocolStorage\UncompressedOrZlib +PresentationContext178 = XAPerformedProcedureProtocolStorage\UncompressedOrZlib +PresentationContext179 = XAXRFGrayscaleSoftcopyPresentationStateStorage\UncompressedOrZlib +PresentationContext180 = XRayRadiationDoseSRStorage\UncompressedOrZlib # ============================================================================ [[Profiles]] diff --git a/dcmnet/etc/storescu.cfg b/dcmnet/etc/storescu.cfg index 412ec319..a912e2de 100644 --- a/dcmnet/etc/storescu.cfg +++ b/dcmnet/etc/storescu.cfg @@ -1,5 +1,5 @@ # -# Copyright (C) 2003-2024, OFFIS e.V. +# Copyright (C) 2003-2025, OFFIS e.V. # All rights reserved. See COPYRIGHT file for details. # # This software and supporting documentation were developed by @@ -81,6 +81,7 @@ TransferSyntax1 = MPEG4HighProfile/Level4.1 # - EncapsulatedUncompressedLittleEndianExplicit # # - DeflatedLittleEndianExplicit +# - DeflatedImageFrameCompression # # - JPEGBaseline # - JPEGExtended:Process2+4 @@ -384,7 +385,9 @@ PresentationContext128 = VideoPhotographicImageStorage\MPEG2 # - VisualAcuityMeasurementsStorage # - VLWholeSlideMicroscopyImageStorage # - VolumeRenderingVolumetricPresentationStateStorage +# - WaveformAcquisitionPresentationStateStorage # - WaveformAnnotationSRStorage +# - WaveformPresentationStateStorage # - WideFieldOphthalmicPhotographyStereographicProjectionImageStorage # - WideFieldOphthalmicPhotography3DCoordinatesImageStorage # - XADefinedProcedureProtocolStorage @@ -419,6 +422,7 @@ PresentationContext128 = VideoPhotographicImageStorage\MPEG2 # - DICONDE_EddyCurrentMultiframeImageStorage # - DICONDE_ThermographyImageStorage # - DICONDE_ThermographyMultiFrameImageStorage +# - DICONDE_UltrasoundWaveformStorage # ============================================================================ [[Profiles]] diff --git a/dcmnet/include/dcmtk/dcmnet/assoc.h b/dcmnet/include/dcmtk/dcmnet/assoc.h index b77d6741..9483b008 100644 --- a/dcmnet/include/dcmtk/dcmnet/assoc.h +++ b/dcmnet/include/dcmtk/dcmnet/assoc.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -218,6 +218,13 @@ struct DCMTK_DCMNET_EXPORT T_ASC_RejectParameters T_ASC_RejectParametersReason reason; }; +struct DCMTK_DCMNET_EXPORT T_ASC_ImplementationIdentification +{ + T_ASC_ImplementationIdentification(); + + DIC_UI ourImplementationClassUID; + DIC_SH ourImplementationVersionName; +}; struct DCMTK_DCMNET_EXPORT T_ASC_Parameters { @@ -295,7 +302,8 @@ DCMTK_DCMNET_EXPORT OFCondition ASC_createAssociationParameters( T_ASC_Parameters ** params, long maxReceivePDUSize, - Sint32 tcpConnectTimeout); + Sint32 tcpConnectTimeout, + const T_ASC_ImplementationIdentification& implIdentification = T_ASC_ImplementationIdentification()); /* * same as before, but uses value of the global dcmConnectionTimeout variable. @@ -697,7 +705,8 @@ ASC_receiveAssociation( unsigned long *associatePDUlength=NULL, OFBool useSecureLayer=OFFalse, DUL_BLOCKOPTIONS block=DUL_BLOCK, - int timeout=0); + int timeout=0, + const T_ASC_ImplementationIdentification& implIdentification = T_ASC_ImplementationIdentification()); DCMTK_DCMNET_EXPORT OFCondition ASC_acknowledgeAssociation( diff --git a/dcmnet/include/dcmtk/dcmnet/dcompat.h b/dcmnet/include/dcmtk/dcmnet/dcompat.h index c083c16a..bb62499b 100644 --- a/dcmnet/include/dcmtk/dcmnet/dcompat.h +++ b/dcmnet/include/dcmtk/dcmnet/dcompat.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -96,9 +96,7 @@ BEGIN_EXTERN_C #ifdef HAVE_SYS_TIME_H #include #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_SOCKET_H #ifndef DCOMPAT_SYS_SOCKET_H_ #define DCOMPAT_SYS_SOCKET_H_ @@ -184,18 +182,6 @@ END_EXTERN_C #endif #endif -#ifndef HAVE_ACCESS - -#ifndef R_OK -#define R_OK 0x01 -#define W_OK 0x02 -#define X_OK 0x04 -#define F_OK 0x08 -#endif - -DCMTK_DCMNET_EXPORT int access(const char* path, int amode); -#else /* HAVE_ACCESS */ - #ifdef _WIN32 /* windows defines access but not the constants */ #ifndef R_OK @@ -206,8 +192,6 @@ DCMTK_DCMNET_EXPORT int access(const char* path, int amode); #endif /* R_OK */ #endif /* _WIN32 */ -#endif /* HAVE_ACCESS */ - #ifdef _WIN32 #define NULL_DEVICE_NAME "nul" #else diff --git a/dcmnet/include/dcmtk/dcmnet/dul.h b/dcmnet/include/dcmtk/dcmnet/dul.h index 745c4998..5c16673b 100644 --- a/dcmnet/include/dcmtk/dcmnet/dul.h +++ b/dcmnet/include/dcmtk/dcmnet/dul.h @@ -130,9 +130,29 @@ extern DCMTK_DCMNET_EXPORT OFGlobal dcmEnableBackwardCompatibilit */ extern DCMTK_DCMNET_EXPORT OFGlobal dcmAssociatePDUSizeLimit; /* default: 1 MB */ +/** TCP/IP protocol family to be used for network connections + */ +enum T_ASC_ProtocolFamily +{ + /// default behaviour. Currently identical to ASC_AF_INET + ASC_AF_Default, + /// only use IPv4 + ASC_AF_INET, + /// only use IPv6 + ASC_AF_INET6, + /// dual-stack operation, automatically select IPv4 or IPv6 + ASC_AF_UNSPEC +}; + +/** TCP/IP protocol family to be supported for incoming network + * connections in association acceptors. + */ +extern DCMTK_DCMNET_EXPORT OFGlobal dcmIncomingProtocolFamily; + typedef void DUL_NETWORKKEY; typedef void DUL_ASSOCIATIONKEY; typedef unsigned char DUL_PRESENTATIONCONTEXTID; +typedef void (*DUL_CREATEPROCESS_CALLBACK)(void *); /** pure virtual base class for DUL mode callbacks */ @@ -176,15 +196,6 @@ public: virtual void callback(unsigned long mode) = 0; }; -enum T_ASC_ProtocolFamily -{ - ASC_AF_Default, - ASC_AF_INET, - ASC_AF_INET6, - ASC_AF_UNSPEC -}; - - typedef struct { char applicationContextName[DUL_LEN_NAME + 1]; char callingAPTitle[DUL_LEN_TITLE + 1]; @@ -573,8 +584,10 @@ DCMTK_DCMNET_EXPORT OFCondition DUL_readSocketHandleAsForkedChild(); * The content of this parameter is ignored on Posix platforms but required * on Win32, where the child process is created with CreateProcess and the * command line parameters have to be passed from parent to child. + * @param pointer to a callback function that will be called on Windows + * when a child process has been created using CreateProcessA(). */ -DCMTK_DCMNET_EXPORT void DUL_requestForkOnTransportConnectionReceipt(int argc, char *argv[]); +DCMTK_DCMNET_EXPORT void DUL_requestForkOnTransportConnectionReceipt(int argc, char *argv[], DUL_CREATEPROCESS_CALLBACK cb = NULL); /** this function sets a flag in the association that the current process * is the parent process after a fork() operation and that the association diff --git a/dcmnet/include/dcmtk/dcmnet/scp.h b/dcmnet/include/dcmtk/dcmnet/scp.h index 527ce111..316ec7b1 100644 --- a/dcmnet/include/dcmtk/dcmnet/scp.h +++ b/dcmnet/include/dcmtk/dcmnet/scp.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2009-2023, OFFIS e.V. + * Copyright (C) 2009-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -322,7 +322,7 @@ public: void setACSETimeout(const Uint32 acseTimeout); /** Set the timeout that should be waited for connection requests. - * Only relevant in non-blocking mode (default). + * Only relevant in non-blocking mode (which is not the default). * @param timeout [in] TCP/IP connection timeout in seconds. */ void setConnectionTimeout(const Uint32 timeout); diff --git a/dcmnet/include/dcmtk/dcmnet/scpcfg.h b/dcmnet/include/dcmtk/dcmnet/scpcfg.h index fdbda466..a43172a2 100644 --- a/dcmnet/include/dcmtk/dcmnet/scpcfg.h +++ b/dcmnet/include/dcmtk/dcmnet/scpcfg.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2012-2022, OFFIS e.V. + * Copyright (C) 2012-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -140,6 +140,16 @@ public: OFCondition checkAssociationProfile(const OFString &profileName, OFString& mangledName) const; + /** Set association implementation identification parameters + * @param implIdentification [in] Implementation Class UID and Implementation Version Name + */ + void setImplementationIdentification(const T_ASC_ImplementationIdentification& implIdentification); + + /** Returns association implementation identification parameters + * @return Implementation Class UID and Implementation Version Name + */ + const T_ASC_ImplementationIdentification& getImplementationIdentification() const; + /** Force every association request to be refused by SCP, no matter what the SCU is * offering * @param doRefuse [in] If OFTrue, every association is being refused. DcmSCP's default @@ -187,7 +197,7 @@ public: void setACSETimeout(const Uint32 acseTimeout); /** Set the timeout that should be waited for connection requests. - * Only relevant in non-blocking mode (default). + * Only relevant in non-blocking mode (which is not the default). * @param timeout [in] TCP/IP connection timeout in seconds. */ void setConnectionTimeout(const Uint32 timeout); @@ -360,6 +370,9 @@ protected: /// called "DEFAULT" is used. OFString m_assocCfgProfileName; + /// Implementation Class UID and Implementation Version Name + T_ASC_ImplementationIdentification m_implIdentification; + /// Port on which the SCP is listening for association requests. The default port is 104. Uint16 m_port; diff --git a/dcmnet/include/dcmtk/dcmnet/scppool.h b/dcmnet/include/dcmtk/dcmnet/scppool.h index f6613f94..fe126766 100644 --- a/dcmnet/include/dcmtk/dcmnet/scppool.h +++ b/dcmnet/include/dcmtk/dcmnet/scppool.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2012-2018, OFFIS e.V. + * Copyright (C) 2012-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -83,6 +83,9 @@ public: */ virtual void exit(); + /** Restart the worker thread */ + virtual void rerun(); + protected: /** Protected constructor which is called within the friend class @@ -210,7 +213,7 @@ protected: * exit. * @param result The final result of the thread. */ - void notifyThreadExit(DcmBaseSCPWorker* thread, + void notifyWorkerDone(DcmBaseSCPWorker* thread, OFCondition result); /** Initialize network, i.e. create an instance of T_ASC_Network and set @@ -218,7 +221,7 @@ protected: * @param network The T_ASC_Network pointer to create the instance * @return EC_Normal if there were no errors during initialization. */ - virtual OFCondition initializeNework(T_ASC_Network** network); + virtual OFCondition initializeNetwork(T_ASC_Network** network); private: @@ -257,7 +260,10 @@ private: // OFList m_waiting; /// Current run mode of pool - runmode m_runMode; + volatile runmode m_runMode; + + /// Set m_runMode to SHUTDOWN on return from listen function + void finishListening(); }; /** Implementation of DICOM SCP server pool. The pool waits for incoming @@ -324,6 +330,8 @@ private: } /** Perform SCP's duties on an already accepted (TCP/IP) connection. + * Once done, destroy the association so the worker does not appear blocked + * for the next request in case of reuse. * @param assoc The association to be run * @return Returns EC_Normal if negotiation could take place and no * serious network error has occurred or the given association @@ -331,7 +339,9 @@ private: */ virtual OFCondition workerListen(T_ASC_Association* const assoc) { - return SCP::run(assoc); + OFCondition result = SCP::run(assoc); + SCP::dropAndDestroyAssociation(); + return result; } }; diff --git a/dcmnet/include/dcmtk/dcmnet/scpthrd.h b/dcmnet/include/dcmtk/dcmnet/scpthrd.h index 13b0ed51..9aa9c1ae 100644 --- a/dcmnet/include/dcmtk/dcmnet/scpthrd.h +++ b/dcmnet/include/dcmtk/dcmnet/scpthrd.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2013, OFFIS e.V. + * Copyright (C) 2013-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -70,6 +70,15 @@ public: */ virtual OFCondition setSharedConfig(const DcmSharedSCPConfig& config); +protected: + + /** Drops association and clears internal structures to free memory, + * resets internal m_assoc pointer to NULL so that isConnected() + * will return false after this call. Exposed from DcmSCP in order + * to enforce proper cleanup from derived classes. + */ + virtual void dropAndDestroyAssociation(); + private: /** Private undefined copy constructor. Shall never be called. diff --git a/dcmnet/include/dcmtk/dcmnet/scu.h b/dcmnet/include/dcmtk/dcmnet/scu.h index 73c48dcf..5a83c25a 100644 --- a/dcmnet/include/dcmtk/dcmnet/scu.h +++ b/dcmnet/include/dcmtk/dcmnet/scu.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2008-2024, OFFIS e.V. + * Copyright (C) 2008-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -701,6 +701,11 @@ public: */ void setAssocConfigFileAndProfile(const OFString& filename, const OFString& profile); + /** Set association implementation identification parameters + * @param implIdentification [in] Implementation Class UID and Implementation Version Name + */ + void setImplementationIdentification(const T_ASC_ImplementationIdentification& implIdentification); + /** Set the directory that should be used by the standard C-GET handler to store objects * that come in with the corresponding C-STORE requests * @param storeDir [in] The directory to store to. It is checked in handleSTORERequest() @@ -1076,6 +1081,9 @@ private: /// Configuration file containing association parameters OFString m_assocConfigFile; + /// Implementation Class UID and Implementation Version Name + T_ASC_ImplementationIdentification m_implIdentification; + /// The last DIMSE successfully sent, unresponded DIMSE request T_DIMSE_Message* m_openDIMSERequest; diff --git a/dcmnet/libsrc/assoc.cc b/dcmnet/libsrc/assoc.cc index e86b42a6..5497c0ad 100644 --- a/dcmnet/libsrc/assoc.cc +++ b/dcmnet/libsrc/assoc.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -102,9 +102,7 @@ #ifdef HAVE_SYS_TIME_H #include #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_SELECT_H #include #endif @@ -209,6 +207,22 @@ typedef struct { } T_ASC_ExtendedNegotiationItem; +T_ASC_ImplementationIdentification::T_ASC_ImplementationIdentification() +{ + OFStandard::strlcpy(ourImplementationClassUID, + OFFIS_IMPLEMENTATION_CLASS_UID, + sizeof(ourImplementationClassUID)); + OFStandard::strlcpy(ourImplementationVersionName, + OFFIS_DTK_IMPLEMENTATION_VERSION_NAME, + sizeof(ourImplementationVersionName)); + + if (strlen(OFFIS_DTK_IMPLEMENTATION_VERSION_NAME) > 16) + { + DCMNET_WARN("DICOM implementation version name too long: " << OFFIS_DTK_IMPLEMENTATION_VERSION_NAME); + } +} + + /* ** Function Bodies */ @@ -274,25 +288,20 @@ ASC_dropNetwork(T_ASC_Network ** network) OFCondition ASC_createAssociationParameters(T_ASC_Parameters ** params, long maxReceivePDUSize, - Sint32 tcpConnectTimeout) + Sint32 tcpConnectTimeout, + const T_ASC_ImplementationIdentification& implIdentification) { - *params = (T_ASC_Parameters *) malloc(sizeof(**params)); if (*params == NULL) return EC_MemoryExhausted; memset((char*)*params, 0, sizeof(**params)); OFStandard::strlcpy((*params)->ourImplementationClassUID, - OFFIS_IMPLEMENTATION_CLASS_UID, + implIdentification.ourImplementationClassUID, sizeof((*params)->ourImplementationClassUID)); OFStandard::strlcpy((*params)->ourImplementationVersionName, - OFFIS_DTK_IMPLEMENTATION_VERSION_NAME, + implIdentification.ourImplementationVersionName, sizeof((*params)->ourImplementationVersionName)); - if (strlen(OFFIS_DTK_IMPLEMENTATION_VERSION_NAME) > 16) - { - DCMNET_WARN("DICOM implementation version name too long: " << OFFIS_DTK_IMPLEMENTATION_VERSION_NAME); - } - OFStandard::strlcpy((*params)->DULparams.callingImplementationClassUID, (*params)->ourImplementationClassUID, DICOM_UI_LENGTH + 1); OFStandard::strlcpy((*params)->DULparams.callingImplementationVersionName, @@ -1753,7 +1762,8 @@ ASC_receiveAssociation(T_ASC_Network * network, unsigned long *associatePDUlength, OFBool useSecureLayer, DUL_BLOCKOPTIONS block, - int timeout) + int timeout, + const T_ASC_ImplementationIdentification& implIdentification) { T_ASC_Parameters *params; DUL_ASSOCIATIONKEY *DULassociation; @@ -1763,7 +1773,7 @@ ASC_receiveAssociation(T_ASC_Network * network, int retrieveRawPDU = 0; if (associatePDU && associatePDUlength) retrieveRawPDU = 1; - OFCondition cond = ASC_createAssociationParameters(¶ms, maxReceivePDUSize, dcmConnectionTimeout.get()); + OFCondition cond = ASC_createAssociationParameters(¶ms, maxReceivePDUSize, dcmConnectionTimeout.get(), implIdentification); if (cond.bad()) return cond; cond = ASC_setTransportLayerType(params, useSecureLayer); diff --git a/dcmnet/libsrc/dcmtrans.cc b/dcmnet/libsrc/dcmtrans.cc index 15e3ed99..9212bfb5 100644 --- a/dcmnet/libsrc/dcmtrans.cc +++ b/dcmnet/libsrc/dcmtrans.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1998-2024, OFFIS e.V. + * Copyright (C) 1998-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -38,9 +38,7 @@ BEGIN_EXTERN_C #ifdef HAVE_SYS_TIME_H #include #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_SELECT_H #include #endif @@ -147,38 +145,28 @@ DcmTransportConnection::~DcmTransportConnection() OFBool DcmTransportConnection::safeSelectReadableAssociation(DcmTransportConnection *connections[], int connCount, int timeout) { - int numberOfRounds = timeout+1; - if (numberOfRounds < 0) numberOfRounds = 0xFFFF; /* a long time */ - + double dtimeout = timeout; OFBool found = OFFalse; - OFBool firstRound = OFTrue; - int timeToWait=0; + OFTimer tmr; int i=0; - while ((numberOfRounds > 0)&&(! found)) + while ((!found) && (tmr.getDiff() < dtimeout)) { - if (firstRound) - { - timeToWait = 0; - firstRound = OFFalse; - } - else timeToWait = 1; for (i=0; inetworkDataAvailable(timeToWait)) + + if (connections[i]->networkDataAvailable(0)) { i = connCount; /* break out of for loop */ found = OFTrue; /* break out of while loop */ } - timeToWait = 0; } } /* for */ - if (timeToWait == 1) return OFFalse; /* all entries NULL */ - numberOfRounds--; + if (!found) OFStandard::milliSleep(10); } /* while */ - /* number of rounds == 0 (timeout over), do final check */ + /* Readable connection found or timeout reached. Do final check. */ found = OFFalse; for (i=0; inetworkDataAvailable(0)) found = OFTrue; else connections[i]=NULL; } } + return found; } diff --git a/dcmnet/libsrc/dcompat.cc b/dcmnet/libsrc/dcompat.cc index 0c939956..fa715c9f 100644 --- a/dcmnet/libsrc/dcompat.cc +++ b/dcmnet/libsrc/dcompat.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -89,12 +89,8 @@ #include "dcmtk/dcmnet/diutil.h" BEGIN_EXTERN_C -#ifdef HAVE_SYS_TYPES_H #include -#endif -#ifdef HAVE_SYS_STAT_H #include -#endif #ifdef HAVE_SYS_UTSNAME_H #include #endif @@ -264,30 +260,6 @@ int dcmtk_flock(int fd, int operation) #endif /* HAVE_FLOCK */ -#ifndef HAVE_ACCESS - -/* -** The access function is OS dependent. -*/ - -#if defined(macintosh) || defined(_WIN32) -int access(const char* path, int /* amode */) -{ - int rc; - struct stat buf; - - rc = stat(path, &buf); - - /* WARNING - ** on the macintosh if a file is there we can do anything with it except - ** if it is locked or on a read only filesystem. Trying to find out about - ** that is too complicated at the moment. - */ - return rc; -} -#endif - -#endif /* HAVE_ACCESS */ DCMTK_DCMNET_EXPORT void dcmtk_plockerr(const char *s) { diff --git a/dcmnet/libsrc/dimcancl.cc b/dcmnet/libsrc/dimcancl.cc index 088394a1..1f520282 100644 --- a/dcmnet/libsrc/dimcancl.cc +++ b/dcmnet/libsrc/dimcancl.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -81,10 +81,7 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ -#ifdef HAVE_FCNTL_H #include -#endif - #include "dcmtk/dcmnet/diutil.h" #include "dcmtk/dcmnet/dimse.h" /* always include the module header */ #include "dcmtk/dcmnet/cond.h" diff --git a/dcmnet/libsrc/dimcmd.cc b/dcmnet/libsrc/dimcmd.cc index a0165510..da760ce1 100644 --- a/dcmnet/libsrc/dimcmd.cc +++ b/dcmnet/libsrc/dimcmd.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -81,10 +81,7 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ -#ifdef HAVE_FCNTL_H #include -#endif - #include "dcmtk/dcmdata/dcdatset.h" #include "dcmtk/dcmdata/dcdeftag.h" #include "dcmtk/dcmdata/dcelem.h" diff --git a/dcmnet/libsrc/dimdump.cc b/dcmnet/libsrc/dimdump.cc index 2853573e..eee10efa 100644 --- a/dcmnet/libsrc/dimdump.cc +++ b/dcmnet/libsrc/dimdump.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1993-2021, OFFIS e.V. + * Copyright (C) 1993-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -30,10 +30,7 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ -#ifdef HAVE_FCNTL_H #include -#endif - #include "dcmtk/dcmnet/diutil.h" #include "dcmtk/dcmnet/dimse.h" /* always include the module header */ #include "dcmtk/dcmdata/dcuid.h" diff --git a/dcmnet/libsrc/dimfind.cc b/dcmnet/libsrc/dimfind.cc index 53ee40f2..c0c7bef4 100644 --- a/dcmnet/libsrc/dimfind.cc +++ b/dcmnet/libsrc/dimfind.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -81,10 +81,7 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ -#ifdef HAVE_FCNTL_H #include -#endif - #include "dcmtk/dcmnet/diutil.h" #include "dcmtk/dcmnet/dimse.h" /* always include the module header */ #include "dcmtk/dcmnet/cond.h" diff --git a/dcmnet/libsrc/dimget.cc b/dcmnet/libsrc/dimget.cc index 485293ed..4ee937c0 100644 --- a/dcmnet/libsrc/dimget.cc +++ b/dcmnet/libsrc/dimget.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1998-2024, OFFIS e.V. + * Copyright (C) 1998-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -24,10 +24,7 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ -#ifdef HAVE_FCNTL_H #include -#endif - #include "dcmtk/dcmnet/diutil.h" #include "dcmtk/dcmnet/dimse.h" /* always include the module header */ #include "dcmtk/dcmnet/cond.h" diff --git a/dcmnet/libsrc/dimmove.cc b/dcmnet/libsrc/dimmove.cc index 5a1b7684..79cf1d7f 100644 --- a/dcmnet/libsrc/dimmove.cc +++ b/dcmnet/libsrc/dimmove.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -81,11 +81,7 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "dcmtk/ofstd/oftimer.h" - -#ifdef HAVE_FCNTL_H #include -#endif - #include "dcmtk/dcmnet/diutil.h" #include "dcmtk/dcmnet/dimse.h" /* always include the module header */ #include "dcmtk/dcmnet/cond.h" diff --git a/dcmnet/libsrc/dimse.cc b/dcmnet/libsrc/dimse.cc index 70ed62aa..38d011ff 100644 --- a/dcmnet/libsrc/dimse.cc +++ b/dcmnet/libsrc/dimse.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -80,10 +80,7 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ -#ifdef HAVE_FCNTL_H #include -#endif - #include "dcmtk/dcmnet/diutil.h" #include "dcmtk/dcmnet/dimse.h" /* always include the module header */ #include "dcmtk/dcmnet/cond.h" @@ -338,6 +335,7 @@ getTransferSyntax( #ifdef WITH_ZLIB case EXS_DeflatedLittleEndianExplicit: #endif + case EXS_DeflatedImageFrameCompression: case EXS_JPEGLSLossless: case EXS_JPEGLSLossy: case EXS_JPEG2000LosslessOnly: diff --git a/dcmnet/libsrc/dimstore.cc b/dcmnet/libsrc/dimstore.cc index 6976616d..414db271 100644 --- a/dcmnet/libsrc/dimstore.cc +++ b/dcmnet/libsrc/dimstore.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -81,10 +81,7 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ -#ifdef HAVE_FCNTL_H #include -#endif - #include "dcmtk/dcmnet/diutil.h" #include "dcmtk/dcmnet/dimse.h" /* always include the module header */ #include "dcmtk/dcmnet/cond.h" diff --git a/dcmnet/libsrc/diutil.cc b/dcmnet/libsrc/diutil.cc index 3f1b39ff..8ced9f23 100644 --- a/dcmnet/libsrc/diutil.cc +++ b/dcmnet/libsrc/diutil.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -78,13 +78,8 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ -#ifdef HAVE_SYS_TYPES_H #include -#endif -#ifdef HAVE_SYS_STAT_H #include -#endif - #include "dcmtk/ofstd/ofstd.h" #include "dcmtk/dcmnet/diutil.h" #include "dcmtk/dcmdata/dcdatset.h" diff --git a/dcmnet/libsrc/dstorscp.cc b/dcmnet/libsrc/dstorscp.cc index fb1856db..36293664 100644 --- a/dcmnet/libsrc/dstorscp.cc +++ b/dcmnet/libsrc/dstorscp.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2013-2022, OFFIS e.V. + * Copyright (C) 2013-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -185,6 +185,7 @@ OFCondition DcmStorageSCP::handleIncomingCommand(T_DIMSE_Message *incomingMsg, { // handle incoming C-ECHO request status = handleECHORequest(incomingMsg->msg.CEchoRQ, presInfo.presentationContextID); + // TODO: need to handle invalid abstract syntax } else if (incomingMsg->CommandField == DIMSE_C_STORE_RQ) { diff --git a/dcmnet/libsrc/dul.cc b/dcmnet/libsrc/dul.cc index b821b3d2..9fba5f02 100644 --- a/dcmnet/libsrc/dul.cc +++ b/dcmnet/libsrc/dul.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -83,9 +83,7 @@ BEGIN_EXTERN_C #ifdef HAVE_SYS_TIME_H #include #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif /* sys/socket.h included via "dcmtk/ofstd/ofsockad.h" - needed for Ultrix */ #ifdef HAVE_SYS_SELECT_H #include @@ -103,9 +101,7 @@ BEGIN_EXTERN_C #include /* for hosts_ctl */ int dcmtk_hosts_access(struct request_info *req); #endif -#ifdef HAVE_FCNTL_H #include /* for FD_CLOEXEC */ -#endif /* declare extern "C" typedef for signal handler function pointer */ typedef void(*mySIG_TYP)(int); @@ -139,6 +135,7 @@ OFGlobal dcmExternalSocketHandle(DCMNET_INVALID_SOCKET); OFGlobal dcmTCPWrapperDaemonName((const char *)NULL); OFGlobal dcmEnableBackwardCompatibility(0); OFGlobal dcmAssociatePDUSizeLimit(0x100000); +OFGlobal dcmIncomingProtocolFamily(ASC_AF_Default); static int networkInitialized = 0; @@ -196,6 +193,7 @@ static OFBool shouldFork = OFFalse; #ifdef _WIN32 static char **command_argv = NULL; static int command_argc = 0; +static DUL_CREATEPROCESS_CALLBACK create_process_callback = NULL; #endif OFBool DUL_processIsForkedChild() @@ -246,16 +244,18 @@ OFCondition DUL_readSocketHandleAsForkedChild() } -void DUL_requestForkOnTransportConnectionReceipt(int argc, char *argv[]) +void DUL_requestForkOnTransportConnectionReceipt(int argc, char *argv[], DUL_CREATEPROCESS_CALLBACK cb) { shouldFork = OFTrue; #ifdef _WIN32 command_argc = argc; command_argv = argv; + create_process_callback = cb; #else // Work around "Unused parameters" (void) argc; (void) argv; + (void) cb; #endif } @@ -1566,7 +1566,7 @@ receiveTransportConnectionTCP(PRIVATE_NETWORKKEY ** network, struct timeval timeout_val; socklen_t len; int nfound, connected; - struct sockaddr from; + struct sockaddr_storage from; struct linger sockarg; #ifdef HAVE_FORK @@ -1590,7 +1590,7 @@ receiveTransportConnectionTCP(PRIVATE_NETWORKKEY ** network, connected = 1; len = sizeof(from); - if (getpeername(sock, &from, &len)) + if (getpeername(sock, OFreinterpret_cast(struct sockaddr*, &from), &len)) { OFOStringStream stream; stream << "TCP Initialization Error: " << OFStandard::getLastNetworkErrorCode().message() @@ -1695,7 +1695,7 @@ receiveTransportConnectionTCP(PRIVATE_NETWORKKEY ** network, len = sizeof(from); do { - sock = accept((*network)->networkSpecific.TCP.listenSocket, &from, &len); + sock = accept((*network)->networkSpecific.TCP.listenSocket, OFreinterpret_cast(struct sockaddr*, &from), &len); #ifdef _WIN32 } while (sock == INVALID_SOCKET && WSAGetLastError() == WSAEINTR); if (sock == INVALID_SOCKET) @@ -1863,7 +1863,18 @@ receiveTransportConnectionTCP(PRIVATE_NETWORKKEY ** network, // close handles in PROCESS_INFORMATION structure // and our local copy of the socket handle. CloseHandle(hParentProcessHandle); - CloseHandle(pi.hProcess); + + if (create_process_callback) + { + // hand the process handle over to the callback process. + // The user code is now responsible for closing the handle when not needed anymore. + (*create_process_callback)(pi.hProcess); + } + else + { + CloseHandle(pi.hProcess); + } + CloseHandle(pi.hThread); closesocket(sock); @@ -1915,8 +1926,10 @@ receiveTransportConnectionTCP(PRIVATE_NETWORKKEY ** network, if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse, sizeof(reuse)) < 0) #endif { - OFString msg = "TCP Initialization Error: "; - msg += OFStandard::getLastNetworkErrorCode().message(); + OFOStringStream stream; + stream << "TCP Initialization Error: " << OFStandard::getLastNetworkErrorCode().message() + << ", setsockopt failed on socket " << sock << OFStringStream_ends; + OFSTRINGSTREAM_GETOFSTRING(stream, msg) return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); } setTCPBufferLength(sock); @@ -1959,8 +1972,10 @@ receiveTransportConnectionTCP(PRIVATE_NETWORKKEY ** network, #endif if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&tcpNoDelay, sizeof(tcpNoDelay)) < 0) { - OFString msg = "TCP Initialization Error: "; - msg += OFStandard::getLastNetworkErrorCode().message(); + OFOStringStream stream; + stream << "TCP Initialization Error: " << OFStandard::getLastNetworkErrorCode().message() + << ", setsockopt failed on socket " << sock << OFStringStream_ends; + OFSTRINGSTREAM_GETOFSTRING(stream, msg) return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); } #ifdef DISABLE_NAGLE_ALGORITHM @@ -1969,28 +1984,49 @@ receiveTransportConnectionTCP(PRIVATE_NETWORKKEY ** network, #endif } - // create string containing numerical IP address. + + // lookup string containing numerical IP address. OFString client_dns_name; - char client_ip_address[20]; - OFStandard::snprintf(client_ip_address, sizeof(client_ip_address), "%-d.%-d.%-d.%-d", // this code is ugly but thread safe - ((int) from.sa_data[2]) & 0xff, - ((int) from.sa_data[3]) & 0xff, - ((int) from.sa_data[4]) & 0xff, - ((int) from.sa_data[5]) & 0xff); + OFString client_ip_address; + char host[NI_MAXHOST]; // buffer for numerical IP address in text form + char serv[NI_MAXSERV]; // buffer for port number in text form + if (getnameinfo((struct sockaddr*)&from, len, + host, sizeof(host), + serv, sizeof(serv), + NI_NUMERICHOST | NI_NUMERICSERV) == 0) + { + client_ip_address = host; + } + else + { + client_ip_address = "unknown address"; + } + // lookup hostname if (! dcmDisableGethostbyaddr.get()) - client_dns_name = OFStandard::getHostnameByAddress(&from.sa_data[2], sizeof(struct in_addr), AF_INET); + { + struct sockaddr *saddr = OFreinterpret_cast(struct sockaddr*, &from); + if (saddr->sa_family == AF_INET6) + { + client_dns_name = OFStandard::getHostnameByAddress(&saddr->sa_data[6], sizeof(struct in6_addr), AF_INET6); + } + else if (saddr->sa_family == AF_INET) + { + client_dns_name = OFStandard::getHostnameByAddress(&saddr->sa_data[2], sizeof(struct in_addr), AF_INET); + } + } if (client_dns_name.length() == 0) { // reverse DNS lookup disabled or host not found, use numerical address - OFStandard::strlcpy(params->callingPresentationAddress, client_ip_address, + OFStandard::strlcpy(params->callingPresentationAddress, client_ip_address.c_str(), sizeof(params->callingPresentationAddress)); - OFStandard::strlcpy((*association)->remoteNode, client_ip_address, sizeof((*association)->remoteNode)); - DCMNET_DEBUG("Association Received: " << params->callingPresentationAddress ); + OFStandard::strlcpy((*association)->remoteNode, client_ip_address.c_str(), + sizeof((*association)->remoteNode)); } else { + // use either full domain name (if DUL_FULLDOMAINNAME is set) or just the hostname char node[260]; if ((*network)->options & DUL_FULLDOMAINNAME) OFStandard::strlcpy(node, client_dns_name.c_str(), sizeof(node)); @@ -2000,8 +2036,8 @@ receiveTransportConnectionTCP(PRIVATE_NETWORKKEY ** network, } OFStandard::strlcpy((*association)->remoteNode, node, sizeof((*association)->remoteNode)); OFStandard::strlcpy(params->callingPresentationAddress, node, sizeof(params->callingPresentationAddress)); - DCMNET_DEBUG("Association Received: " << params->callingPresentationAddress ); } + DCMNET_DEBUG("Association Received: " << params->callingPresentationAddress ); #ifdef WITH_TCPWRAPPER const char *daemon = dcmTCPWrapperDaemonName.get(); @@ -2014,7 +2050,7 @@ receiveTransportConnectionTCP(PRIVATE_NETWORKKEY ** network, struct request_info request; request_init(&request, RQ_CLIENT_NAME, client_dns_name.c_str(), 0); - request_set(&request, RQ_CLIENT_ADDR, client_ip_address, 0); + request_set(&request, RQ_CLIENT_ADDR, client_ip_address.c_str(), 0); request_set(&request, RQ_USER, STRING_UNKNOWN, 0); request_set(&request, RQ_DAEMON, daemon, 0); @@ -2168,100 +2204,192 @@ initializeNetworkTCP(PRIVATE_NETWORKKEY ** key, void *parameter) ((*key)->applicationFunction & DICOM_APPLICATION_ACCEPTOR) && (! processIsForkedChild)) { - - socklen_t length; - #ifdef _WIN32 - SOCKET sock; + SOCKET sock = INVALID_SOCKET; #else - int sock; + int sock = -1; #endif - struct sockaddr_in server; - /* Create socket for Internet type communication */ - (*key)->networkSpecific.TCP.port = *(int *) parameter; + // extract port number from the generic parameter passed to this function + (*key)->networkSpecific.TCP.port = *(int *) parameter; + int port = (*key)->networkSpecific.TCP.port; - // Create socket and prevent leakage of the open socket to processes called with exec() - // by using SOCK_CLOEXEC (where available) or FD_CLOEXEC (POSIX.1-2008) + // determine which protocol family we should support + T_ASC_ProtocolFamily supportedFamily = dcmIncomingProtocolFamily.get(); + int af = 0; + switch(supportedFamily) + { + case ASC_AF_Default: + af = AF_INET; // for now the default is to use IPv4 only + break; + case ASC_AF_INET: + af = AF_INET; // IPv4 only + break; + case ASC_AF_INET6: + af = AF_INET6; // IPv6 only + break; + case ASC_AF_UNSPEC: + af = AF_INET6; // IPv6 in dual-stack mode + break; + } + + // Create socket and prevent leakage of the open socket to processes called with exec() + // by using SOCK_CLOEXEC (where available) or FD_CLOEXEC (POSIX.1-2008) #ifdef SOCK_CLOEXEC - (*key)->networkSpecific.TCP.listenSocket = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); - sock = (*key)->networkSpecific.TCP.listenSocket; + sock = socket(af, SOCK_STREAM | SOCK_CLOEXEC, 0); #elif defined(FD_CLOEXEC) - (*key)->networkSpecific.TCP.listenSocket = socket(AF_INET, SOCK_STREAM, 0); - sock = (*key)->networkSpecific.TCP.listenSocket; - if (sock >= 0) - { - int flags = fcntl(sock, F_GETFD, 0); - fcntl(sock, F_SETFD, FD_CLOEXEC | flags); - } + sock = socket(af, SOCK_STREAM, 0); + if (sock >= 0) + { + int flags = fcntl(sock, F_GETFD, 0); + fcntl(sock, F_SETFD, FD_CLOEXEC | flags); + } #else - (*key)->networkSpecific.TCP.listenSocket = socket(AF_INET, SOCK_STREAM, 0); - sock = (*key)->networkSpecific.TCP.listenSocket; + sock = socket(af, SOCK_STREAM, 0); #endif +#ifdef _WIN32 + if (sock == INVALID_SOCKET) +#else + if (sock < 0) +#endif + { + OFString msg = "TCP Initialization Error: "; + msg += OFStandard::getLastNetworkErrorCode().message(); + return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); + } + + // store socket handle + (*key)->networkSpecific.TCP.listenSocket = sock; + // make sure that no other process is already bound to the same port. + // In this case we want bind() to fail. #ifdef _WIN32 - if (sock == INVALID_SOCKET) + if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &reuse, sizeof(reuse)) < 0) #else - if (sock < 0) + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse, sizeof(reuse)) < 0) #endif - { - OFString msg = "TCP Initialization Error: "; - msg += OFStandard::getLastNetworkErrorCode().message(); - return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); - } - reuse = 1; + { #ifdef _WIN32 - if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &reuse, sizeof(reuse)) < 0) + (void) shutdown(sock, 1 /* SD_SEND */); + closesocket(sock); + (*key)->networkSpecific.TCP.listenSocket = INVALID_SOCKET; #else - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse, sizeof(reuse)) < 0) + close(sock); + (*key)->networkSpecific.TCP.listenSocket = -1; #endif - { - OFString msg = "TCP Initialization Error: "; - msg += OFStandard::getLastNetworkErrorCode().message(); - return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); - } + OFString msg = "TCP Initialization Error: "; + msg += OFStandard::getLastNetworkErrorCode().message(); + return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); + } - /* Name socket using wildcards */ - server.sin_family = AF_INET; - server.sin_addr.s_addr = INADDR_ANY; - server.sin_port = htons(OFstatic_cast(Uint16, ((*key)->networkSpecific.TCP.port))); - if (bind(sock, (struct sockaddr *) & server, sizeof(server))) - { - OFString msg = "TCP Initialization Error: "; - msg += OFStandard::getLastNetworkErrorCode().message(); - return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); - } - /* Find out assigned port number and print it out */ - length = sizeof(server); - if (getsockname(sock, (struct sockaddr *) &server, &length)) - { - OFString msg = "TCP Initialization Error: "; - msg += OFStandard::getLastNetworkErrorCode().message(); - return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); - } + // configure the IPv6 socket either as IPv6 only or as a dual stack socket + // that accepts IPv4 via v4-mapped + if (af == AF_INET6) + { + int off = (supportedFamily == ASC_AF_UNSPEC) ? 0 : 1; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off))) + { +#ifdef _WIN32 + (void) shutdown(sock, 1 /* SD_SEND */); + closesocket(sock); + (*key)->networkSpecific.TCP.listenSocket = INVALID_SOCKET; +#else + close(sock); + (*key)->networkSpecific.TCP.listenSocket = -1; +#endif + OFString msg = "TCP Initialization Error: "; + msg += OFStandard::getLastNetworkErrorCode().message(); + return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); + } + } - /* If port 0 was specified by the client, the OS has assigned an unused port. */ - if ((*key)->networkSpecific.TCP.port == 0) { - const u_short assignedPort = ntohs(server.sin_port); - (*key)->networkSpecific.TCP.port = assignedPort; - *(int *) parameter = assignedPort; - } + // bind the socket to the given port number + OFBool bind_ok = OFFalse; + if (af == AF_INET6) { + struct sockaddr_in6 server6; + memset(&server6, 0, sizeof(server6)); + server6.sin6_family = AF_INET6; + server6.sin6_addr = in6addr_any; + server6.sin6_port = htons(OFstatic_cast(Uint16, port)); + if (bind(sock, (struct sockaddr *)&server6, sizeof(server6)) == 0) bind_ok = OFTrue; + } else if (af == AF_INET) { + struct sockaddr_in server; + memset(&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(OFstatic_cast(Uint16, port)); + if (bind(sock, (struct sockaddr *)&server, sizeof(server)) == 0) bind_ok = OFTrue; + } - sockarg.l_onoff = 0; - if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &sockarg, sizeof(sockarg)) < 0) - { - OFString msg = "TCP Initialization Error: "; - msg += OFStandard::getLastNetworkErrorCode().message(); - return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); - } + if (! bind_ok) { +#ifdef _WIN32 + (void) shutdown(sock, 1 /* SD_SEND */); + closesocket(sock); + (*key)->networkSpecific.TCP.listenSocket = INVALID_SOCKET; +#else + close(sock); + (*key)->networkSpecific.TCP.listenSocket = -1; +#endif + OFString msg = "TCP Initialization Error: "; + msg += OFStandard::getLastNetworkErrorCode().message(); + return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); + } + + // disable linger mode, i.e. when closing the socket, + // do not wait until all queued messages for the socket have been + // successfully sent or the linger timeout has been reached. + sockarg.l_onoff = 0; + if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &sockarg, sizeof(sockarg)) < 0) + { +#ifdef _WIN32 + (void) shutdown(sock, 1 /* SD_SEND */); + closesocket(sock); + (*key)->networkSpecific.TCP.listenSocket = INVALID_SOCKET; +#else + close(sock); + (*key)->networkSpecific.TCP.listenSocket = -1; +#endif + OFString msg = "TCP Initialization Error: "; + msg += OFStandard::getLastNetworkErrorCode().message(); + return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); + } - /* Listen on the socket */ - if (listen(sock, PRV_LISTENBACKLOG) < 0) - { - OFString msg = "TCP Initialization Error: "; - msg += OFStandard::getLastNetworkErrorCode().message(); - return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); - } + // listen for incoming connections, limit backlog to at most + // PRV_LISTENBACKLOG (i.e. 50) incoming connection request + if (listen(sock, PRV_LISTENBACKLOG) < 0) + { +#ifdef _WIN32 + (void) shutdown(sock, 1 /* SD_SEND */); + closesocket(sock); + (*key)->networkSpecific.TCP.listenSocket = INVALID_SOCKET; +#else + close(sock); + (*key)->networkSpecific.TCP.listenSocket = -1; +#endif + OFString msg = "TCP Initialization Error: "; + msg += OFStandard::getLastNetworkErrorCode().message(); + return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); + } + + // If port 0 was specified by the caller, the OS has assigned an unused port. + // store this port and return it in the parameter passed to this function + if (port == 0) + { + if (af == AF_INET6) + { + struct sockaddr_in6 sin6; socklen_t sl = sizeof(sin6); + if (getsockname(sock, (struct sockaddr *)&sin6, &sl) == 0) + port = ntohs(sin6.sin6_port); + } + else + { + struct sockaddr_in sin; socklen_t sl = sizeof(sin); + if (getsockname(sock, (struct sockaddr *)&sin, &sl) == 0) + port = ntohs(sin.sin_port); + } + (*key)->networkSpecific.TCP.port = port; + *(int *) parameter = port; + } } (*key)->networkSpecific.TCP.tLayer = new DcmTransportLayer(); diff --git a/dcmnet/libsrc/dulextra.cc b/dcmnet/libsrc/dulextra.cc index 2b52471b..a32c5ca6 100644 --- a/dcmnet/libsrc/dulextra.cc +++ b/dcmnet/libsrc/dulextra.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -82,9 +82,7 @@ #ifdef HAVE_SYS_TIME_H #include #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_SELECT_H #include #endif @@ -147,7 +145,7 @@ DUL_associationWaiting(DUL_NETWORKKEY * callerNet, int timeout) t.tv_sec = timeout; t.tv_usec = 0; #ifdef DCMTK_HAVE_POLL - struct pollfd pfd[] = + struct pollfd pfd[] = { { s, POLLIN, 0 } }; diff --git a/dcmnet/libsrc/dulfsm.cc b/dcmnet/libsrc/dulfsm.cc index c9ba5e61..060361ec 100644 --- a/dcmnet/libsrc/dulfsm.cc +++ b/dcmnet/libsrc/dulfsm.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1994-2024, OFFIS e.V. + * Copyright (C) 1994-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were partly developed by @@ -74,15 +74,11 @@ #ifdef HAVE_SYS_TIME_H #include #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_SELECT_H #include -#ifdef HAVE_FCNTL_H #include #endif -#endif BEGIN_EXTERN_C #ifdef HAVE_NETINET_IN_SYSTM_H @@ -2463,9 +2459,8 @@ requestAssociationTCP(PRIVATE_NETWORKKEY ** network, if ((*association)->connection) delete (*association)->connection; (*association)->connection = NULL; - char buf[256]; OFString msg = "TCP Initialization Error: "; - msg += OFStandard::strerror(socketError, buf, sizeof(buf)); + msg += OFStandard::getLastNetworkErrorCode().message(); return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); } } @@ -2490,6 +2485,7 @@ requestAssociationTCP(PRIVATE_NETWORKKEY ** network, { // an error other than timeout in non-blocking mode has occurred, // either in connect() or in select(). + OFerror_code ec = OFStandard::getLastNetworkErrorCode(); #ifdef HAVE_WINSOCK_H (void) shutdown(s, 1 /* SD_SEND */); (void) closesocket(s); @@ -2501,7 +2497,7 @@ requestAssociationTCP(PRIVATE_NETWORKKEY ** network, (*association)->connection = NULL; OFString msg = "TCP Initialization Error: "; - msg += OFStandard::getLastNetworkErrorCode().message(); + msg += ec.message(); return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str()); } else { // success - we've opened a TCP transport connection @@ -3550,7 +3546,7 @@ readPDUHeadTCP(PRIVATE_ASSOCIATIONKEY ** association, if (!found) { char buf[256]; - OFStandard::snprintf(buf, sizeof(buf), "Unrecognized PDU type: %2x", *type); + OFStandard::snprintf(buf, sizeof(buf), "Unrecognized PDU type: %2.2x", *type); return makeDcmnetCondition(DULC_UNRECOGNIZEDPDUTYPE, OF_error, buf); } diff --git a/dcmnet/libsrc/scp.cc b/dcmnet/libsrc/scp.cc index eb4aef99..0b84d00b 100644 --- a/dcmnet/libsrc/scp.cc +++ b/dcmnet/libsrc/scp.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2009-2024, OFFIS e.V. + * Copyright (C) 2009-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -386,14 +386,16 @@ OFCondition DcmSCP::waitForAssociationRQ(T_ASC_Network* network) OFBool useSecureLayer = m_cfg->transportLayerEnabled(); // Listen to a socket for timeout seconds and wait for an association request - OFCondition cond = ASC_receiveAssociation(network, - &m_assoc, - m_cfg->getMaxReceivePDULength(), - NULL, - NULL, - useSecureLayer, - m_cfg->getConnectionBlockingMode(), - OFstatic_cast(int, timeout)); + OFCondition cond = ASC_receiveAssociation( + network, + &m_assoc, + m_cfg->getMaxReceivePDULength(), + NULL, + NULL, + useSecureLayer, + m_cfg->getConnectionBlockingMode(), + OFstatic_cast(int, timeout), + m_cfg->getImplementationIdentification()); // In case of a timeout in non-blocking mode, call notifier (and return // to main event loop later) diff --git a/dcmnet/libsrc/scpcfg.cc b/dcmnet/libsrc/scpcfg.cc index f057d55e..88631759 100644 --- a/dcmnet/libsrc/scpcfg.cc +++ b/dcmnet/libsrc/scpcfg.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2012-2024, OFFIS e.V. + * Copyright (C) 2012-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -28,6 +28,7 @@ DcmSCPConfig::DcmSCPConfig() : m_assocConfig(), m_assocCfgProfileName("DEFAULT"), + m_implIdentification(), m_port(104), m_aetitle("DCMTK_SCP"), m_refuseAssociation(OFFalse), @@ -56,6 +57,7 @@ DcmSCPConfig::~DcmSCPConfig() DcmSCPConfig::DcmSCPConfig(const DcmSCPConfig &old) : m_assocConfig(old.m_assocConfig), m_assocCfgProfileName(old.m_assocCfgProfileName), + m_implIdentification(old.m_implIdentification), m_port(old.m_port), m_aetitle(old.m_aetitle), m_refuseAssociation(old.m_refuseAssociation), @@ -82,6 +84,7 @@ DcmSCPConfig& DcmSCPConfig::operator=(const DcmSCPConfig &obj) { m_assocConfig = obj.m_assocConfig; // performs deep copy m_assocCfgProfileName = obj.m_assocCfgProfileName; + m_implIdentification = obj.m_implIdentification; m_port = obj.m_port; m_aetitle = obj.m_aetitle; m_refuseAssociation = obj.m_refuseAssociation; @@ -401,6 +404,18 @@ OFCondition DcmSCPConfig::checkAssociationProfile(const OFString& profileName, } +void DcmSCPConfig::setImplementationIdentification(const T_ASC_ImplementationIdentification& implIdentification) +{ + m_implIdentification = implIdentification; +} + + +const T_ASC_ImplementationIdentification& DcmSCPConfig::getImplementationIdentification() const +{ + return m_implIdentification; +} + + OFCondition DcmSCPConfig::addPresentationContext(const OFString &abstractSyntax, const OFList &xferSyntaxes, const T_ASC_SC_ROLE role, diff --git a/dcmnet/libsrc/scppool.cc b/dcmnet/libsrc/scppool.cc index a134065d..c4a5d61f 100644 --- a/dcmnet/libsrc/scppool.cc +++ b/dcmnet/libsrc/scppool.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2012-2020, OFFIS e.V. + * Copyright (C) 2012-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -38,7 +38,7 @@ DcmBaseSCPPool::DcmBaseSCPPool() m_workersIdle(), m_cfg(), m_maxWorkers(5), - m_runMode( LISTEN ) + m_runMode( SHUTDOWN ) // LISTEN mode will be set, once actual listening will be started. // not implemented yet: m_workersBusyTimeout(60), // not implemented yet: m_waiting(), { @@ -48,6 +48,27 @@ DcmBaseSCPPool::DcmBaseSCPPool() DcmBaseSCPPool::~DcmBaseSCPPool() { + // Wait that we are in SHUTDOWN mode + // Let busy threads finish their work and get moved from the busy list to the idle list. + while (m_runMode != SHUTDOWN || DcmBaseSCPPool::numThreads(OFTrue) != 0) + { + DCMNET_DEBUG("DcmBaseSCPPool: Destructor called, waiting for runMode to become SHUTDOWN (currently " << m_runMode << ")"); + OFStandard::forceSleep(1); + } + DCMNET_DEBUG("DcmBaseSCPPool: Destructor called, cleaning up " << m_workersIdle.size() << " idle worker threads"); + // Now all workers must be in idle list which will be joined and deleted now. + // Since we are in SHUTDOWN mode, no other thread can modify the lists anymore. + m_criticalSection.lock(); + size_t count = 1; + for (OFListIterator(DcmBaseSCPWorker*) it = m_workersIdle.begin(); it != m_workersIdle.end(); ++it) + { + DCMNET_DEBUG("DcmBaseSCPPool: Joining and deleting idle worker thread " << count << ", #" << (*it)->threadID()); + (*it)->join(); + delete (*it); + count++; + } + m_criticalSection.unlock(); + DCMNET_DEBUG("DcmBaseSCPPool: Destructor finished"); } // ---------------------------------------------------------------------------- @@ -61,9 +82,12 @@ OFCondition DcmBaseSCPPool::listen() /* Initialize network, i.e. create an instance of T_ASC_Network*. */ T_ASC_Network *network = NULL; - OFCondition cond = initializeNework(&network); + OFCondition cond = initializeNetwork(&network); if(cond.bad()) + { + finishListening(); return cond; + } /* As long as all is fine (or we have been to busy handling last connection request) keep listening */ while ( m_runMode == LISTEN && ( cond.good() || (cond == NET_EC_SCPBusy) ) ) @@ -75,8 +99,16 @@ OFCondition DcmBaseSCPPool::listen() OFBool useSecureLayer = m_cfg.transportLayerEnabled(); // Listen to a socket for timeout seconds for an association request, accepts TCP connection. - cond = ASC_receiveAssociation( network, &assoc, m_cfg.getMaxReceivePDULength(), NULL, NULL, useSecureLayer, - m_cfg.getConnectionBlockingMode(), OFstatic_cast(int, m_cfg.getConnectionTimeout()) ); + cond = ASC_receiveAssociation( + network, + &assoc, + m_cfg.getMaxReceivePDULength(), + NULL, + NULL, + useSecureLayer, + m_cfg.getConnectionBlockingMode(), + OFstatic_cast(int, m_cfg.getConnectionTimeout()), + m_cfg.getImplementationIdentification()); /* If we have a connection request, try to find/create a worker to handle it */ if (cond.good()) @@ -115,26 +147,17 @@ OFCondition DcmBaseSCPPool::listen() cond = EC_Normal; } } + // Log why we left the listen loop + if (cond.bad()) + DCMNET_DEBUG("DcmBaseSCPPool: Leaving listen loop due to error: " << cond.text()); + else if (cond == NET_EC_SCPBusy) + DCMNET_DEBUG("DcmBaseSCPPool: Leaving listen loop due to too many concurrent connections (busy)."); + else if (m_runMode == STOP) + DCMNET_DEBUG("DcmBaseSCPPool: Leaving listen loop due to stop request."); + else + DCMNET_DEBUG("DcmBaseSCPPool: Leaving listen loop, result: " << cond.text() << " (runMode: " << m_runMode << ")"); - m_criticalSection.lock(); - m_runMode = SHUTDOWN; - - // iterate over all busy workers, join their threads and delete them. - for - ( - OFListIterator( DcmBaseSCPPool::DcmBaseSCPWorker* ) it = m_workersBusy.begin(); - it != m_workersBusy.end(); - ++it - ) - { - m_criticalSection.unlock(); - (*it)->join(); - delete *it; - m_criticalSection.lock(); - } - - m_workersBusy.clear(); - m_criticalSection.unlock(); + finishListening(); /* In the end, clean up the rest of the memory and drop network */ ASC_dropNetwork(&network); @@ -188,6 +211,7 @@ OFCondition DcmBaseSCPPool::runAssociation(T_ASC_Association *assoc, /* Try to find idle worker thread */ OFCondition result = EC_Normal; DcmBaseSCPWorker *chosen = NULL; + OFBool isNewWorker = OFFalse; /* Do we have idle worker threads that can handle the association? */ m_criticalSection.lock(); @@ -195,6 +219,7 @@ OFCondition DcmBaseSCPPool::runAssociation(T_ASC_Association *assoc, { if (m_workersBusy.size() >= m_maxWorkers) { + DCMNET_DEBUG("DcmBaseSCPPool: Maximum number of busy worker threads reached (" << m_maxWorkers << "), cannot handle incoming association"); /* No idle workers and maximum of busy workers reached? Return busy */ result = NET_EC_SCPBusy; } @@ -211,12 +236,15 @@ OFCondition DcmBaseSCPPool::runAssociation(T_ASC_Association *assoc, m_workersBusy.push_back(worker); worker->setSharedConfig(sharedConfig); chosen = worker; + isNewWorker = OFTrue; + DCMNET_DEBUG("DcmBaseSCPPool: Created new worker thread, now " << m_workersBusy.size() << " busy threads total"); } } } /* Else we have idle workers, use one of them */ else { + DCMNET_DEBUG("DcmBaseSCPPool: Reusing existing idle DcmSCP worker thread #" << m_workersIdle.front()->threadID()); chosen = m_workersIdle.front(); m_workersIdle.pop_front(); m_workersBusy.push_back(chosen); @@ -230,13 +258,19 @@ OFCondition DcmBaseSCPPool::runAssociation(T_ASC_Association *assoc, result = chosen->setAssociation(assoc); } /* Start the thread */ - if (result.good()) + if (isNewWorker && result.good()) { if (chosen->start() != 0) { result = NET_EC_CannotStartSCPThread; } } + else if (result.good()) + { + // If we reuse an existing worker thread, it might be waiting for an association. + // Wake it up now. + chosen->rerun(); + } /* Return to listen loop */ return result; } @@ -274,23 +308,29 @@ DcmSCPConfig& DcmBaseSCPPool::getConfig() // ---------------------------------------------------------------------------- -void DcmBaseSCPPool::notifyThreadExit(DcmBaseSCPPool::DcmBaseSCPWorker* thread, +void DcmBaseSCPPool::notifyWorkerDone(DcmBaseSCPPool::DcmBaseSCPWorker* thread, OFCondition result) { m_criticalSection.lock(); - if( m_runMode != SHUTDOWN ) + if (result.bad()) { DCMNET_DEBUG("DcmBaseSCPPool: Worker thread #" << thread->threadID() << " exited with error: " << result.text()); - m_workersBusy.remove(thread); - delete thread; - thread = NULL; } + else + { + DCMNET_DEBUG("DcmBaseSCPPool: Worker thread #" << thread->threadID() << " finished successfully."); + } + // Move thread from busy to idle lists + m_workersBusy.remove(thread); + m_workersIdle.push_back(thread); + DCMNET_DEBUG("DcmBaseSCPPool: Put worker thread #" << thread->threadID() << " back to idle list; now " + << m_workersBusy.size() << " busy and " << m_workersIdle.size() << " idle worker threads."); m_criticalSection.unlock(); } // ---------------------------------------------------------------------------- -OFCondition DcmBaseSCPPool::initializeNework(T_ASC_Network** network) +OFCondition DcmBaseSCPPool::initializeNetwork(T_ASC_Network** network) { OFCondition cond = ASC_initializeNetwork(NET_ACCEPTOR, OFstatic_cast(int, m_cfg.getPort()), m_cfg.getACSETimeout(), network); if (cond.good()) @@ -308,6 +348,7 @@ OFCondition DcmBaseSCPPool::initializeNework(T_ASC_Network** network) return cond; } + /* *********************************************************************** */ /* DcmBaseSCPPool::BaseSCPWorker class */ /* *********************************************************************** */ @@ -322,7 +363,6 @@ DcmBaseSCPPool::DcmBaseSCPWorker::DcmBaseSCPWorker(DcmBaseSCPPool& pool) DcmBaseSCPPool::DcmBaseSCPWorker::~DcmBaseSCPWorker() { - // do nothing } // ---------------------------------------------------------------------------- @@ -330,7 +370,10 @@ DcmBaseSCPPool::DcmBaseSCPWorker::~DcmBaseSCPWorker() OFCondition DcmBaseSCPPool::DcmBaseSCPWorker::setAssociation(T_ASC_Association* assoc) { if (busy()) + { + DCMNET_DEBUG("DcmBaseSCPPool: Worker thread #" << threadID() << " is already busy, cannot set new association"); return NET_EC_AlreadyConnected; + } if ( (m_assoc != NULL) || (assoc == NULL) ) return DIMSE_ILLEGALASSOCIATION; @@ -347,18 +390,17 @@ void DcmBaseSCPPool::DcmBaseSCPWorker::run() if(!m_assoc) { DCMNET_ERROR("DcmBaseSCPPool: Worker thread #" << threadID() << " received run command but has no association, exiting"); - m_pool.notifyThreadExit(this, ASC_NULLKEY); - thread_exit(); + m_pool.notifyWorkerDone(this, ASC_NULLKEY); } else { T_ASC_Association *param = m_assoc; m_assoc = NULL; result = workerListen(param); - DCMNET_DEBUG("DcmBaseSCPPool: Worker thread #" << threadID() << " returns with code: " << result.text() ); + m_pool.notifyWorkerDone(this, result); + DCMNET_DEBUG("DcmBaseSCPPool: Worker thread #" << threadID() << " finished handling association, dropping and destroying its association"); + DCMNET_DEBUG("DcmBaseSCPPool: Worker thread #" << threadID() << " returns with result: " << result.text() ); } - m_pool.notifyThreadExit(this, result); - thread_exit(); return; } @@ -369,4 +411,22 @@ void DcmBaseSCPPool::DcmBaseSCPWorker::exit() thread_exit(); } +// ---------------------------------------------------------------------------- + +void DcmBaseSCPPool::DcmBaseSCPWorker::rerun() +{ + DcmBaseSCPPool::DcmBaseSCPWorker::run(); +} + +// ---------------------------------------------------------------------------- + +void DcmBaseSCPPool::finishListening() +{ + m_criticalSection.lock(); + // Set run mode to SHUTDOWN which signals destructor that its time to clean up + m_runMode = SHUTDOWN; + m_criticalSection.unlock(); +} + + #endif // WITH_THREADS diff --git a/dcmnet/libsrc/scpthrd.cc b/dcmnet/libsrc/scpthrd.cc index a6a2e61f..a63d564b 100644 --- a/dcmnet/libsrc/scpthrd.cc +++ b/dcmnet/libsrc/scpthrd.cc @@ -57,6 +57,11 @@ OFCondition DcmThreadSCP::setSharedConfig(const DcmSharedSCPConfig& config) return EC_Normal; } +void DcmThreadSCP::dropAndDestroyAssociation() +{ + DcmSCP::dropAndDestroyAssociation(); +} + // ---------------------------------------------------------------------------- OFCondition DcmThreadSCP::run(T_ASC_Association* incomingAssoc) diff --git a/dcmnet/libsrc/scu.cc b/dcmnet/libsrc/scu.cc index 31d93c11..fa3a8635 100644 --- a/dcmnet/libsrc/scu.cc +++ b/dcmnet/libsrc/scu.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2008-2024, OFFIS e.V. + * Copyright (C) 2008-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -124,7 +124,7 @@ OFCondition DcmSCU::initNetwork() } /* initialize association parameters, i.e. create an instance of T_ASC_Parameters*. */ - cond = ASC_createAssociationParameters(&m_params, m_maxReceivePDULength, m_tcpConnectTimeout); + cond = ASC_createAssociationParameters(&m_params, m_maxReceivePDULength, m_tcpConnectTimeout, m_implIdentification); if (cond.bad()) { DCMNET_ERROR(DimseCondition::dump(tempStr, cond)); @@ -2577,6 +2577,11 @@ void DcmSCU::setAssocConfigFileAndProfile(const OFString& filename, const OFStri m_assocConfigProfile = profile; } +void DcmSCU::setImplementationIdentification(const T_ASC_ImplementationIdentification& implIdentification) +{ + m_implIdentification = implIdentification; +} + void DcmSCU::setStorageDir(const OFString& storeDir) { m_storageDir = storeDir; diff --git a/dcmnet/tests/tpool.cc b/dcmnet/tests/tpool.cc index b52036fe..ff3c15ef 100644 --- a/dcmnet/tests/tpool.cc +++ b/dcmnet/tests/tpool.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2013-2014, OFFIS e.V. + * Copyright (C) 2013-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -27,17 +27,44 @@ #include "dcmtk/ofstd/oftest.h" #include "dcmtk/dcmnet/scppool.h" #include "dcmtk/dcmnet/scu.h" +#include "dcmtk/ofstd/ofthread.h" + + +const size_t NUM_THREADS = 20; struct TestSCU : DcmSCU, OFThread { - OFCondition result; + + TestSCU() : m_result() + { + m_resultMutex.lock(); + m_result = EC_IllegalCall; + m_resultMutex.unlock(); + } + + void getResult(OFCondition& result) + { + m_resultMutex.lock(); + result = m_result; + m_resultMutex.unlock(); + } + protected: + void run() { negotiateAssociation(); - result = sendECHORequest(0); + m_resultMutex.lock(); + m_result = sendECHORequest(0); + m_resultMutex.unlock(); releaseAssociation(); } + +private: + + OFCondition m_result; + OFMutex m_resultMutex; + }; struct TestPool : DcmSCPPool<>, OFThread @@ -69,7 +96,7 @@ OFTEST_FLAGS(dcmnet_scp_pool, EF_Slow) // stopAfterCurrentAssociations(). config.setConnectionTimeout(1); - pool.setMaxThreads(20); + pool.setMaxThreads(NUM_THREADS); OFList xfers; xfers.push_back(UID_LittleEndianExplicitTransferSyntax); xfers.push_back(UID_LittleEndianImplicitTransferSyntax); @@ -77,7 +104,7 @@ OFTEST_FLAGS(dcmnet_scp_pool, EF_Slow) pool.start(); - OFVector scus(20); + OFVector scus(NUM_THREADS, NULL); for (OFVector::iterator it1 = scus.begin(); it1 != scus.end(); ++it1) { *it1 = new TestSCU; @@ -91,17 +118,48 @@ OFTEST_FLAGS(dcmnet_scp_pool, EF_Slow) // "ensure" the pool is initialized before any SCU starts connecting to it. The initialization // can take a couple of seconds on older systems, e.g. debian i368. - OFStandard::sleep(5); + OFStandard::forceSleep(5); for (OFVector::const_iterator it2 = scus.begin(); it2 != scus.end(); ++it2) (*it2)->start(); + // Ensure the SCUs have time to connect and send requests also on slow systems + OFStandard::forceSleep(5); for (OFVector::iterator it3 = scus.begin(); it3 != scus.end(); ++it3) { + OFCondition scuResult; + (*it3)->getResult(scuResult); (*it3)->join(); - OFCHECK((*it3)->result.good()); delete *it3; + (*it3) = NULL; + OFCHECK(scuResult.good()); } + scus.clear(); + + // Second round to check whether thread re-use works inside the pool + for (OFVector::iterator it4 = scus.begin(); it4 != scus.end(); ++it4) + { + *it4 = new TestSCU; + (*it4)->setAETitle("PoolTestSCU"); + (*it4)->setPeerAETitle("PoolTestSCP"); + (*it4)->setPeerHostName("localhost"); + (*it4)->setPeerPort(11112); + (*it4)->addPresentationContext(UID_VerificationSOPClass, xfers); + (*it4)->initNetwork(); + } + + for (OFVector::const_iterator it2 = scus.begin(); it2 != scus.end(); ++it2) + (*it2)->start(); + + for (OFVector::iterator it3 = scus.begin(); it3 != scus.end(); ++it3) + { + OFCondition scuResult; + (*it3)->getResult(scuResult); + OFCHECK(scuResult.good()); + (*it3)->join(); + delete *it3; + } + // Request shutdown. pool.stopAfterCurrentAssociations(); diff --git a/dcmpmap/include/dcmtk/dcmpmap/dpmparametricmapiod.h b/dcmpmap/include/dcmtk/dcmpmap/dpmparametricmapiod.h index 3df1e1aa..e96dcb35 100644 --- a/dcmpmap/include/dcmtk/dcmpmap/dpmparametricmapiod.h +++ b/dcmpmap/include/dcmtk/dcmpmap/dpmparametricmapiod.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2018-2024, Open Connections GmbH + * Copyright (C) 2018-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -177,6 +177,35 @@ public: */ virtual OFCondition write(DcmItem &dataset); + /** If enabled, functional group structure is checked before actual writing + * is performed in the write() method. Checking might be time consuming + * on functional groups with many frames, though disabling might result in + * invalid functional group structures. Disabling should only be done if the + * user knows that the functional groups are valid, wants to to adapt the + * functional groups manually after calling write() or knows what he's doing + * otherwise.
+ * Per default, checking is enabled. + * @param doCheck If OFTrue, checking will be performed. If OFFalse, + * no checks are performed. + */ + virtual void setCheckFGOnWrite(const OFBool doCheck); + + /** Returns whether functional group structure is checked before actual + * writing is performed in the write() method. + * @return OFTrue if checking is performed, OFFalse otherwise + */ + virtual OFBool getCheckFGOnWrite(); + + /** Set whether attribute values should be checked on writing, i.e. if writing + * should fail if attribute values violate their VR, VM, character set or value length. + * A missing but required value is always considered an error, independent of this setting. + * If set to OFFalse, writing will always succeed, even if attribute value constraints + * are violated. A warning instead of an error will be printed to the logger. + * @param doCheck If OFTrue, attribute value errors are handled as errors on writing, if OFFalse + * any errors are ignored. + */ + virtual void setValueCheckOnWrite(const OFBool doCheck); + // -------------------- access --------------------- /** Get Recognizable Visual Features @@ -298,7 +327,7 @@ private: ContentIdentificationMacro m_ContentIdentification; /// Binary frame data - OFVector m_Frames; + OFVector m_Frames; }; diff --git a/dcmpmap/libsrc/Makefile.dep b/dcmpmap/libsrc/Makefile.dep index bc9e4670..82bd6e26 100644 --- a/dcmpmap/libsrc/Makefile.dep +++ b/dcmpmap/libsrc/Makefile.dep @@ -69,6 +69,7 @@ dpmmodparametricmapimage.o: dpmmodparametricmapimage.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h \ @@ -146,6 +147,7 @@ dpmmodparametricmapseries.o: dpmmodparametricmapseries.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h \ @@ -225,6 +227,7 @@ dpmparametricmapbase.o: dpmparametricmapbase.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h \ @@ -349,6 +352,7 @@ dpmparametricmapiod.o: dpmparametricmapiod.cc \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../dcmiod/include/dcmtk/dcmiod/modcommoninstanceref.h \ ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ diff --git a/dcmpmap/libsrc/dpmparametricmapiod.cc b/dcmpmap/libsrc/dpmparametricmapiod.cc index 9301e3fa..4dbf315f 100644 --- a/dcmpmap/libsrc/dpmparametricmapiod.cc +++ b/dcmpmap/libsrc/dpmparametricmapiod.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2016-2024, Open Connections GmbH + * Copyright (C) 2016-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -149,12 +149,10 @@ struct DPMParametricMapIOD::ReadVisitor { for (Uint32 n = 0; n < numFrames; n++) { - DcmIODTypes::Frame* f = new DcmIODTypes::Frame; + DcmIODTypes::Frame* f = new DcmIODTypes::Frame(numBytesFrame); if (f) { - f->length = numBytesFrame; - f->pixData = new Uint8[f->length]; - memcpy(f->pixData, pixData + n*numBytesFrame/2, numBytesFrame); + memcpy(f->m_pixData, pixData + n*numBytesFrame/2, numBytesFrame); map.m_Frames.push_back(f); } else @@ -191,12 +189,10 @@ struct DPMParametricMapIOD::ReadVisitor { for (Uint32 n=0; n < numFrames; n++) { - DcmIODTypes::Frame* f = new DcmIODTypes::Frame; - if (f) + DcmIODTypes::Frame* f = new DcmIODTypes::Frame(numBytesFrame); + if (f && f->m_pixData) { - f->length = numBytesFrame; - f->pixData = new Uint8[f->length]; - memcpy(f->pixData, pixData + n*numBytesFrame/4, numBytesFrame); + memcpy(f->m_pixData, pixData + n*numBytesFrame/4, numBytesFrame); map.m_Frames.push_back(f); } else @@ -233,12 +229,10 @@ struct DPMParametricMapIOD::ReadVisitor { for (Uint16 n=0; n < numFrames; n++) { - DcmIODTypes::Frame* f = new DcmIODTypes::Frame; - if (f) + DcmIODTypes::Frame* f = new DcmIODTypes::Frame(numBytesFrame); + if (f && f->m_pixData) { - f->length = numBytesFrame; - f->pixData = new Uint8[f->length]; - memcpy(f->pixData, pixData + n*numBytesFrame/8, numBytesFrame); + memcpy(f->m_pixData, pixData + n*numBytesFrame/8, numBytesFrame); map.m_Frames.push_back(f); } else @@ -309,7 +303,7 @@ struct DPMParametricMapIOD::WriteVisitor map.getRows(rows); map.getColumns(cols); const size_t numFrames = map.m_Frames.size(); - const size_t numBytesFrame = map.m_Frames[0]->length; + const size_t numBytesFrame = map.m_Frames[0]->getLengthInBytes(); const size_t numPixelsFrame = rows * cols; // Creates the correct pixel data element, based on the image pixel module used. // I.e. For integer data, the "Pixel Data" element is used, i.e. the DcmElement type @@ -322,7 +316,7 @@ struct DPMParametricMapIOD::WriteVisitor { for (size_t f = 0; f < numFrames; ++f) { - memcpy(ptr, map.m_Frames[f]->pixData, numBytesFrame); + memcpy(ptr, map.m_Frames[f]->getPixelData(), numBytesFrame); ptr += numPixelsFrame; } OFCondition result = element.put(item); @@ -591,12 +585,10 @@ OFCondition DPMParametricMapIOD::Frames::addFrame(PixelType* data, { if (!perFrameInformation.empty()) { - OFunique_ptr f(new DcmIODTypes::Frame); + OFunique_ptr > f(new DcmIODTypes::Frame(numPixels * sizeof(PixelType))); if (f) { - f->length = numPixels * sizeof(PixelType); - f->pixData = new Uint8[f->length]; - memcpy(f->pixData, data, f->length); + memcpy(f->m_pixData, data, f->getLengthInBytes()); m_Map.m_Frames.push_back(f.release()); OFVector::const_iterator fg = perFrameInformation.begin(); while ( result.good() && (fg != perFrameInformation.end()) ) @@ -624,7 +616,11 @@ PixelType* DPMParametricMapIOD::Frames::getFrame(const size_t frameNu { if (frameNumber < m_Map.m_Frames.size()) { - return (PixelType*)(m_Map.m_Frames[frameNumber]->pixData); + DcmIODTypes::Frame * f = OFstatic_cast(DcmIODTypes::Frame*, m_Map.m_Frames[frameNumber]); + if (f) + { + return f->m_pixData; + } } return NULL; } @@ -913,6 +909,34 @@ OFBool DPMParametricMapIOD::check() } +OFBool DPMParametricMapIOD::getCheckFGOnWrite() +{ + return m_FGInterface.getCheckOnWrite(); +} + + +void DPMParametricMapIOD::setCheckFGOnWrite(const OFBool doCheck) +{ + m_FGInterface.setCheckOnWrite(doCheck); +} + + +void DPMParametricMapIOD::setValueCheckOnWrite(const OFBool doCheck) +{ + // Forward setting into all modules + m_DPMParametricMapSeriesModule.setValueCheckOnWrite(doCheck); + m_IODEnhGeneralEquipmentModule.setValueCheckOnWrite(doCheck); + m_IODMultiframeDimensionModule.setValueCheckOnWrite(doCheck); + m_IODAcquisitionContextModule.setValueCheckOnWrite(doCheck); + m_IODCommonInstanceReferenceModule.setValueCheckOnWrite(doCheck); + + m_IODMultiFrameFGModule.setValueCheckOnWrite(doCheck); + m_DPMParametricMapImageModule.setValueCheckOnWrite(doCheck); + m_DPMParametricMapSeriesModule.setValueCheckOnWrite(doCheck); + DcmIODImage::setValueCheckOnWrite(doCheck); +} + + OFCondition DPMParametricMapIOD::getColumns(Uint16& cols) { return getImagePixel().getColumns(cols); diff --git a/dcmpstat/apps/CMakeLists.txt b/dcmpstat/apps/CMakeLists.txt index 0209208a..fa173c34 100644 --- a/dcmpstat/apps/CMakeLists.txt +++ b/dcmpstat/apps/CMakeLists.txt @@ -8,9 +8,9 @@ endforeach() # make sure executables are linked to the corresponding libraries foreach(PROGRAM dcmp2pgm dcmprscp dcmprscu dcmpsmk dcmpschk dcmpsprt dcmpsrcv dcmpssnd) - DCMTK_TARGET_LINK_MODULES(${PROGRAM} dcmpstat dcmdsig dcmsr dcmimage dcmimgle dcmqrdb dcmnet dcmtls dcmdata oflog ofstd) + DCMTK_TARGET_LINK_MODULES(${PROGRAM} dcmpstat dcmdsig dcmsr dcmimage dcmimgle dcmqrdb dcmtls) endforeach() foreach(PROGRAM dcmmkcrv dcmmklut) - DCMTK_TARGET_LINK_MODULES(${PROGRAM} dcmdsig dcmsr dcmimage dcmimgle dcmdata oflog ofstd) + DCMTK_TARGET_LINK_MODULES(${PROGRAM} dcmdsig dcmsr dcmimage dcmimgle) endforeach() diff --git a/dcmpstat/apps/Makefile.dep b/dcmpstat/apps/Makefile.dep index 7364504c..292224c7 100644 --- a/dcmpstat/apps/Makefile.dep +++ b/dcmpstat/apps/Makefile.dep @@ -916,7 +916,8 @@ dcmpschk.o: dcmpschk.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmpstat/dvpsall.h ../include/dcmtk/dcmpstat/dvpsgal.h \ ../include/dcmtk/dcmpstat/dvpscul.h ../include/dcmtk/dcmpstat/dvpsvll.h \ ../include/dcmtk/dcmpstat/dvpsvwl.h ../include/dcmtk/dcmpstat/dvpsdal.h \ - ../include/dcmtk/dcmpstat/dvpssvl.h ../include/dcmtk/dcmpstat/dvpspl.h + ../include/dcmtk/dcmpstat/dvpssvl.h ../include/dcmtk/dcmpstat/dvpspl.h \ + ../include/dcmtk/dcmpstat/dvpsri.h dcmpsmk.o: dcmpsmk.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ ../../ofstd/include/dcmtk/ofstd/oftypes.h \ @@ -1056,7 +1057,7 @@ dcmpsmk.o: dcmpsmk.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmpstat/dvpscul.h ../include/dcmtk/dcmpstat/dvpsvll.h \ ../include/dcmtk/dcmpstat/dvpsvwl.h ../include/dcmtk/dcmpstat/dvpsdal.h \ ../include/dcmtk/dcmpstat/dvpssvl.h ../include/dcmtk/dcmpstat/dvpspl.h \ - ../include/dcmtk/dcmpstat/dvpshlp.h + ../include/dcmtk/dcmpstat/dvpshlp.h ../include/dcmtk/dcmpstat/dvpsri.h dcmpsprt.o: dcmpsprt.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofstream.h \ ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ @@ -1385,7 +1386,7 @@ dcmpsrcv.o: dcmpsrcv.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmpstat/dvpsgal.h ../include/dcmtk/dcmpstat/dvpscul.h \ ../include/dcmtk/dcmpstat/dvpsvll.h ../include/dcmtk/dcmpstat/dvpsvwl.h \ ../include/dcmtk/dcmpstat/dvpsdal.h ../include/dcmtk/dcmpstat/dvpssvl.h \ - ../include/dcmtk/dcmpstat/dvpspl.h \ + ../include/dcmtk/dcmpstat/dvpspl.h ../include/dcmtk/dcmpstat/dvpsri.h \ ../../dcmtls/include/dcmtk/dcmtls/tlstrans.h \ ../../dcmnet/include/dcmtk/dcmnet/dcmtrans.h \ ../../dcmtls/include/dcmtk/dcmtls/tlsdefin.h \ diff --git a/dcmpstat/apps/dcmprscp.cc b/dcmpstat/apps/dcmprscp.cc index 77f4ca5a..052afbd5 100644 --- a/dcmpstat/apps/dcmprscp.cc +++ b/dcmpstat/apps/dcmprscp.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -22,9 +22,7 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include /* for O_RDONLY */ -#endif END_EXTERN_C #include "dcmtk/ofstd/ofstream.h" @@ -253,6 +251,8 @@ int main(int argc, char *argv[]) unsigned short targetPort = dvi.getTargetPort(opt_printer); OFBool targetDisableNewVRs = dvi.getTargetDisableNewVRs(opt_printer); OFBool targetUseTLS = dvi.getTargetUseTLS(opt_printer); + T_ASC_ProtocolFamily targetProtocol = dvi.getTargetProtocol(opt_printer); + dcmIncomingProtocolFamily.set(targetProtocol); if (targetPort == 0) { diff --git a/dcmpstat/apps/dcmprscu.cc b/dcmpstat/apps/dcmprscu.cc index 94659cf1..dacdc55d 100644 --- a/dcmpstat/apps/dcmprscu.cc +++ b/dcmpstat/apps/dcmprscu.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999-2024, OFFIS e.V. + * Copyright (C) 1999-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -37,9 +37,7 @@ BEGIN_EXTERN_C #ifdef HAVE_IO_H #include #endif -#ifdef HAVE_FCNTL_H #include /* for O_RDONLY */ -#endif END_EXTERN_C #include "dcmtk/ofstd/ofstream.h" diff --git a/dcmpstat/apps/dcmpschk.cc b/dcmpstat/apps/dcmpschk.cc index 697c2f4e..4f0be022 100644 --- a/dcmpstat/apps/dcmpschk.cc +++ b/dcmpstat/apps/dcmpschk.cc @@ -28,6 +28,7 @@ #include "dcmtk/dcmdata/dctk.h" /* for class DcmDataset */ #include "dcmtk/dcmnet/dul.h" #include "dcmtk/dcmpstat/dcmpstat.h" /* for DcmPresentationState */ +#include "dcmtk/dcmpstat/dvpsri.h" /* for dcmPresentationStateValidationMode */ #include "dcmtk/ofstd/ofstd.h" #ifdef WITH_ZLIB @@ -911,7 +912,7 @@ static int checkfile(const char *filename) } #define SHORTCOL 3 -#define LONGCOL 12 +#define LONGCOL 18 int main(int argc, char *argv[]) { @@ -936,6 +937,11 @@ int main(int argc, char *argv[]) cmd.addOption("--version", "print version information and exit", OFCommandLine::AF_Exclusive); OFLog::addOptions(cmd); + cmd.addGroup("validation options:"); + cmd.addOption("--validate-std", "images referenced by GSPS must belong to the\nsame SOP class (default)"); + cmd.addOption("--validate-related", "images referenced by GSPS may belong to related\n'for presentation' and 'for processing' SOP class"); + cmd.addOption("--validate-relaxed", "images referenced by GSPS may be any SOP class"); + /* evaluate command line */ prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); if (app.parseCommandLine(cmd, argc, argv)) @@ -956,6 +962,13 @@ int main(int argc, char *argv[]) } } + /* validation options */ + cmd.beginOptionBlock(); + if (cmd.findOption("--validate-std")) dcmPresentationStateValidationMode.set(DVPSReferencedImage::CVM_standard); + if (cmd.findOption("--validate-related")) dcmPresentationStateValidationMode.set(DVPSReferencedImage::CVM_accept_Presentation_and_Processing); + if (cmd.findOption("--validate-relaxed")) dcmPresentationStateValidationMode.set(DVPSReferencedImage::CVM_accept_all); + cmd.endOptionBlock(); + /* options */ OFLog::configureFromCommandLine(cmd, app); } @@ -963,6 +976,7 @@ int main(int argc, char *argv[]) /* print resource identifier */ OFLOG_DEBUG(dcmpschkLogger, rcsid << OFendl); + int paramCount = cmd.getParamCount(); for (int param=1; param <= paramCount; param++) { diff --git a/dcmpstat/apps/dcmpsmk.cc b/dcmpstat/apps/dcmpsmk.cc index 57a914a7..d40491f2 100644 --- a/dcmpstat/apps/dcmpsmk.cc +++ b/dcmpstat/apps/dcmpsmk.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1998-2022, OFFIS e.V. + * Copyright (C) 1998-2024, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -31,6 +31,7 @@ #include "dcmtk/dcmnet/dul.h" #include "dcmtk/dcmpstat/dcmpstat.h" #include "dcmtk/dcmpstat/dvpshlp.h" +#include "dcmtk/dcmpstat/dvpsri.h" /* for dcmPresentationStateValidationMode */ #ifdef WITH_ZLIB #include /* for zlibVersion() */ @@ -138,6 +139,11 @@ int main(int argc, char *argv[]) cmd.addOption("--location-media", "-lm", 2, "[f]ilesetID, fileset[UID]: string", "image located on storage medium"); + cmd.addGroup("validation options:", LONGCOL, SHORTCOL + 2); + cmd.addOption("--validate-std", "images referenced by GSPS must belong to the\nsame SOP class (default)"); + cmd.addOption("--validate-related", "images referenced by GSPS may belong to related\n'for presentation' and 'for processing'\nSOP class"); + cmd.addOption("--validate-relaxed", "images referenced by GSPS may be any SOP class"); + cmd.addGroup("output options:"); cmd.addSubGroup("output transfer syntax:"); cmd.addOption("--write-xfer-same", "+t=", "write with same TS as image file (default)"); @@ -242,6 +248,13 @@ int main(int argc, char *argv[]) } cmd.endOptionBlock(); + /* validation options */ + cmd.beginOptionBlock(); + if (cmd.findOption("--validate-std")) dcmPresentationStateValidationMode.set(DVPSReferencedImage::CVM_standard); + if (cmd.findOption("--validate-related")) dcmPresentationStateValidationMode.set(DVPSReferencedImage::CVM_accept_Presentation_and_Processing); + if (cmd.findOption("--validate-relaxed")) dcmPresentationStateValidationMode.set(DVPSReferencedImage::CVM_accept_all); + cmd.endOptionBlock(); + cmd.beginOptionBlock(); if (cmd.findOption("--write-xfer-same")) opt_oxfer = EXS_Unknown; if (cmd.findOption("--write-xfer-little")) opt_oxfer = EXS_LittleEndianExplicit; diff --git a/dcmpstat/apps/dcmpsrcv.cc b/dcmpstat/apps/dcmpsrcv.cc index 4e765479..1a2bca77 100644 --- a/dcmpstat/apps/dcmpsrcv.cc +++ b/dcmpstat/apps/dcmpsrcv.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1999-2024, OFFIS e.V. + * Copyright (C) 1999-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -22,15 +22,9 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include /* for O_RDONLY */ -#endif -#ifdef HAVE_SYS_TYPES_H #include /* required for sys/stat.h */ -#endif -#ifdef HAVE_SYS_STAT_H #include /* for stat, fstat */ -#endif END_EXTERN_C #include "dcmtk/dcmpstat/dvpsdef.h" /* for constants */ @@ -47,6 +41,7 @@ END_EXTERN_C #include "dcmtk/dcmnet/dcmlayer.h" #include "dcmtk/dcmdata/dcfilefo.h" #include "dcmtk/dcmpstat/dcmpstat.h" +#include "dcmtk/dcmpstat/dvpsri.h" /* for dcmPresentationStateValidationMode */ #ifdef WITH_OPENSSL #include "dcmtk/dcmtls/tlstrans.h" @@ -854,11 +849,26 @@ int main(int argc, char *argv[]) unsigned short networkPort = dvi.getTargetPort(opt_cfgID); unsigned long networkMaxPDU = dvi.getTargetMaxPDU(opt_cfgID); const char *networkAETitle = dvi.getTargetAETitle(opt_cfgID); + const char *validationMode = dvi.getTargetValidationMode(opt_cfgID); if (networkAETitle==NULL) networkAETitle = dvi.getNetworkAETitle(); unsigned short messagePort = dvi.getMessagePort(); /* port number for IPC */ OFBool keepMessagePortOpen = dvi.getMessagePortKeepOpen(); OFBool useTLS = dvi.getTargetUseTLS(opt_cfgID); OFBool notifyTermination = OFTrue; // notify IPC server of application termination + T_ASC_ProtocolFamily targetProtocol = dvi.getTargetProtocol(opt_cfgID); + dcmIncomingProtocolFamily.set(targetProtocol); + + if (validationMode) + { + OFString vmode(validationMode); + if (vmode == "STD") dcmPresentationStateValidationMode.set(DVPSReferencedImage::CVM_standard); + else if (vmode == "RELATED") dcmPresentationStateValidationMode.set(DVPSReferencedImage::CVM_accept_Presentation_and_Processing); + else if (vmode == "RELAXED") dcmPresentationStateValidationMode.set(DVPSReferencedImage::CVM_accept_all); + else + { + OFLOG_WARN(dcmpsrcvLogger, "unknown validation mode '" << vmode << "', ignoring"); + } + } #ifdef WITH_OPENSSL /* TLS directory */ diff --git a/dcmpstat/apps/dcmpssnd.cc b/dcmpstat/apps/dcmpssnd.cc index 6877b1d3..d5186167 100644 --- a/dcmpstat/apps/dcmpssnd.cc +++ b/dcmpstat/apps/dcmpssnd.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1999-2024, OFFIS e.V. + * Copyright (C) 1999-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -22,15 +22,9 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include /* for O_RDONLY */ -#endif -#ifdef HAVE_SYS_TYPES_H #include /* required for sys/stat.h */ -#endif -#ifdef HAVE_SYS_STAT_H #include /* for stat, fstat */ -#endif END_EXTERN_C #include "dcmtk/dcmpstat/dvpsdef.h" /* for constants */ @@ -90,9 +84,9 @@ static OFCondition sendImage(T_ASC_Association *assoc, const char *sopClass, con #ifdef LOCK_IMAGE_FILES /* shared lock image file */ #ifdef O_BINARY - int lockfd = open(imgFile, O_RDONLY | O_BINARY, 0666); + int lockfd = open(imgFile, O_RDONLY | O_BINARY); #else - int lockfd = open(imgFile, O_RDONLY, 0666); + int lockfd = open(imgFile, O_RDONLY); #endif if (lockfd < 0) { diff --git a/dcmpstat/docs/dcmmkcrv.man b/dcmpstat/docs/dcmmkcrv.man index ea26aa01..cc769f17 100644 --- a/dcmpstat/docs/dcmmkcrv.man +++ b/dcmpstat/docs/dcmmkcrv.man @@ -178,6 +178,6 @@ It is an error if no data dictionary can be loaded. \section dcmmkcrv_copyright COPYRIGHT -Copyright (C) 1998-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1998-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmpstat/docs/dcmmklut.man b/dcmpstat/docs/dcmmklut.man index 59dd270e..60bd8701 100644 --- a/dcmpstat/docs/dcmmklut.man +++ b/dcmpstat/docs/dcmmklut.man @@ -248,6 +248,6 @@ It is an error if no data dictionary can be loaded. \section dcmmklut_copyright COPYRIGHT -Copyright (C) 1998-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1998-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmpstat/docs/dcmp2pgm.man b/dcmpstat/docs/dcmp2pgm.man index bb80a727..80a89b64 100644 --- a/dcmpstat/docs/dcmp2pgm.man +++ b/dcmpstat/docs/dcmp2pgm.man @@ -170,6 +170,6 @@ It is an error if no data dictionary can be loaded. \section dcmp2pgm_copyright COPYRIGHT -Copyright (C) 1998-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1998-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmpstat/docs/dcmprscp.man b/dcmpstat/docs/dcmprscp.man index 15f66117..c87d4f5b 100644 --- a/dcmpstat/docs/dcmprscp.man +++ b/dcmpstat/docs/dcmprscp.man @@ -133,7 +133,8 @@ It is an error if no data dictionary can be loaded. \section dcmprscp_files FILES -\/dcmpstat.cfg, \/printers.cfg - sample configuration files +\/dcmpstat.cfg, \/printers.cfg - sample +configuration files \section dcmprscp_see_also SEE ALSO @@ -141,6 +142,6 @@ It is an error if no data dictionary can be loaded. \section dcmprscp_copyright COPYRIGHT -Copyright (C) 1999-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1999-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmpstat/docs/dcmprscu.man b/dcmpstat/docs/dcmprscu.man index 5d1575e1..ae7e2f0b 100644 --- a/dcmpstat/docs/dcmprscu.man +++ b/dcmpstat/docs/dcmprscu.man @@ -193,7 +193,8 @@ It is an error if no data dictionary can be loaded. \section dcmprscu_files FILES -\/dcmpstat.cfg, \/printers.cfg - sample configuration files +\/dcmpstat.cfg, \/printers.cfg - sample +configuration files \section dcmprscu_see_also SEE ALSO @@ -201,6 +202,6 @@ It is an error if no data dictionary can be loaded. \section dcmprscu_copyright COPYRIGHT -Copyright (C) 1999-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1999-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmpstat/docs/dcmpschk.man b/dcmpstat/docs/dcmpschk.man index 0c813c07..76112dba 100644 --- a/dcmpstat/docs/dcmpschk.man +++ b/dcmpstat/docs/dcmpschk.man @@ -72,6 +72,20 @@ dcmfile-in presentation state file(s) to be checked use config file f for the logger \endverbatim +\subsection dcmpschk_validation_options validation options +\verbatim + --validate-std + images referenced by GSPS must belong to the + same SOP class (default) + + --validate-related + images referenced by GSPS may belong to related + 'for presentation' and 'for processing' SOP class + + --validate-relaxed + images referenced by GSPS may be any SOP class +\endverbatim + \section dcmpschk_logging LOGGING The level of logging output of the various command line tools and underlying @@ -131,6 +145,6 @@ It is an error if no data dictionary can be loaded. \section dcmpschk_copyright COPYRIGHT -Copyright (C) 2000-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2000-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmpstat/docs/dcmpsmk.man b/dcmpstat/docs/dcmpsmk.man index 17e300bf..82f1410c 100644 --- a/dcmpstat/docs/dcmpsmk.man +++ b/dcmpstat/docs/dcmpsmk.man @@ -163,6 +163,20 @@ location of referenced image: image located on storage medium \endverbatim +\subsection dcmpsmk_validation_options validation options +\verbatim + --validate-std + images referenced by GSPS must belong to the + same SOP class (default) + + --validate-related + images referenced by GSPS may belong to related + 'for presentation' and 'for processing' SOP class + + --validate-relaxed + images referenced by GSPS may be any SOP class +\endverbatim + \subsection dcmpsmk_output_options output options \verbatim output transfer syntax: @@ -245,6 +259,6 @@ It is an error if no data dictionary can be loaded. \section dcmpsmk_copyright COPYRIGHT -Copyright (C) 1998-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1998-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmpstat/docs/dcmpsprt.man b/dcmpstat/docs/dcmpsprt.man index 50ddf9fc..321a5700 100644 --- a/dcmpstat/docs/dcmpsprt.man +++ b/dcmpstat/docs/dcmpsprt.man @@ -313,6 +313,6 @@ configuration files \section dcmpsprt_copyright COPYRIGHT -Copyright (C) 1999-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1999-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmpstat/docs/dcmpsrcv.man b/dcmpstat/docs/dcmpsrcv.man index 21a9552b..80fc570c 100644 --- a/dcmpstat/docs/dcmpsrcv.man +++ b/dcmpstat/docs/dcmpsrcv.man @@ -130,6 +130,6 @@ It is an error if no data dictionary can be loaded. \section dcmpsrcv_copyright COPYRIGHT -Copyright (C) 1998-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1998-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmpstat/docs/dcmpssnd.man b/dcmpstat/docs/dcmpssnd.man index 7f48ba44..1f922edb 100644 --- a/dcmpstat/docs/dcmpssnd.man +++ b/dcmpstat/docs/dcmpssnd.man @@ -138,6 +138,6 @@ It is an error if no data dictionary can be loaded. \section dcmpssnd_copyright COPYRIGHT -Copyright (C) 1998-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1998-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmpstat/etc/dcmpstat.cfg b/dcmpstat/etc/dcmpstat.cfg index a64f30f3..a1864f93 100644 --- a/dcmpstat/etc/dcmpstat.cfg +++ b/dcmpstat/etc/dcmpstat.cfg @@ -1,5 +1,5 @@ # -# Copyright (C) 1998-2024, OFFIS e.V. +# Copyright (C) 1998-2025, OFFIS e.V. # All rights reserved. See COPYRIGHT file for details. # # This software and supporting documentation were developed by @@ -270,6 +270,12 @@ MaxAssociations = 16 # than zero. Optional setting, default is Unlimited (wait for TCP/IP timeout). # Timeout = 5 +# IP protocol family to be used. Permitted values are AF_INET (IPv4 only), +# AF_INET6 (IPv6 only) and AF_UNSPEC (for outgoing connections, use DNS +# lookup to determine protocol, for incoming connections use dual-stack mode). +# Optional setting, default is AF_INET. +Protocol = AF_INET + # ---------------------------------------------------------------------------- # This section contains the settings for the graphical user interface (GUI). [GUI] @@ -397,7 +403,8 @@ WarnUnsignedObjectsInSR = true # ---------------------------------------------------------------------------- # # IP protocol family to be used. Permitted values are AF_INET (IPv4 only), -# AF_INET6 (IPv6 only) and AF_UNSPEC (use DNS lookup to determine protocol). +# AF_INET6 (IPv6 only) and AF_UNSPEC (for outgoing connections, use DNS +# lookup to determine protocol, for incoming connections use dual-stack mode). # Optional setting, default is AF_INET. # # protocol = AF_INET @@ -572,6 +579,23 @@ WarnUnsignedObjectsInSR = true # # RandomSeed = random.dat # +# ============================================================================ +# The next settings described below is only used with entries of type +# RECEIVER and have no meaning for other entry types. +# ============================================================================ +# +# Set validation mode for incoming presentation states. +# Default is to enforce the rule that all images referenced by one presentation +# state must belong to the same SOP class. This can be relaxed to accept either +# related 'for presentation' and 'for processing' SOP class references in +# one presentation state, or to not enforce this rule at all. +# Known terms are: +# STD: enforce the one SOP class rule as defined in the standard +# RELATED: permit related 'for presentation'/'for processing' SOP classes +# RELAXED: images referenced by GSPS may be of any SOP class +# Optional setting, default is: STD. +# +# ValidationMode = STD # # ============================================================================ # The next two settings described below are only used with entries of type @@ -999,6 +1023,7 @@ Port = 10004 ImplicitOnly = false DisableNewVRs = false BitPreservingMode = false +ValidationMode = STD # ---------------------------------------------------------------------------- [RECEIVE_2] @@ -1020,6 +1045,7 @@ RandomSeed = receiver.rnd PeerAuthentication = REQUIRE Certificate = sitecert.pem PrivateKey = sitekey.pem +ValidationMode = STD # ---------------------------------------------------------------------------- # Print SCP that supports most options of the DICOM Print protocol and diff --git a/dcmpstat/include/dcmtk/dcmpstat/dviface.h b/dcmpstat/include/dcmtk/dcmpstat/dviface.h index 11a55039..d27ef422 100644 --- a/dcmpstat/include/dcmtk/dcmpstat/dviface.h +++ b/dcmpstat/include/dcmtk/dcmpstat/dviface.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1998-2022, OFFIS e.V. + * Copyright (C) 1998-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by diff --git a/dcmpstat/include/dcmtk/dcmpstat/dvpscf.h b/dcmpstat/include/dcmtk/dcmpstat/dvpscf.h index 155769f0..45da1bce 100644 --- a/dcmpstat/include/dcmtk/dcmpstat/dvpscf.h +++ b/dcmpstat/include/dcmtk/dcmpstat/dvpscf.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1998-2024, OFFIS e.V. + * Copyright (C) 1998-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -160,6 +160,14 @@ class DCMTK_DCMPSTAT_EXPORT DVConfiguration */ OFBool getTargetBitPreservingMode(const char *targetID); + /** returns the VALIDATIONMODE entry for the storage peer with the given + * target ID from the configuration file. + * @param targetID communication target ID, must be one of the target + * identifiers returned by getTargetID(). + * @return entry if present in the config file, NULL otherwise. + */ + const char *getTargetValidationMode(const char *targetID); + /** returns the CORRECTUIDPADDING entry for the storage peer with the given * target ID from the configuration file. * @param targetID communication target ID, must be one of the target @@ -398,7 +406,7 @@ class DCMTK_DCMPSTAT_EXPORT DVConfiguration */ OFBool getTargetPrinterSupportsAnnotationBoxSOPClass(const char *targetID); - /** returns OFTrue if an SESSIONLABELANNOTATION entry for the printer + /** returns OFTrue if an SESSIONLABELANNOTATION entry for the printer * with the given target ID from the configuration file exists and is true. * @param targetID communication target ID, must be one of the target * identifiers returned by getTargetID() for peer type DVPSE_printerAny. @@ -414,7 +422,7 @@ class DCMTK_DCMPSTAT_EXPORT DVConfiguration * @return value if present, NULL otherwise. */ const char *getTargetPrinterAnnotationDisplayFormatID(const char *targetID, OFString& value); - + /** returns the first value from the ANNOTATION entry for the printer * with the given target ID from the configuration file. * @param targetID communication target ID, must be one of the target @@ -422,7 +430,7 @@ class DCMTK_DCMPSTAT_EXPORT DVConfiguration * @return value if present, 0 otherwise. */ Uint16 getTargetPrinterAnnotationPosition(const char *targetID); - + /** returns the number of distinct values (separated by backslash characters) * in the FILMSIZEID entry for the printer with the given * target ID from the configuration file. @@ -604,7 +612,7 @@ class DCMTK_DCMPSTAT_EXPORT DVConfiguration */ OFLogger::LogLevel getLogLevel(); - /** returns the port on which the GUI application accepts notification + /** returns the port on which the GUI application accepts notification * messages from the network processes. * Value is taken from the section GENERAL/APPLICATION/MESSAGEPORT * in the config file. @@ -664,6 +672,12 @@ class DCMTK_DCMPSTAT_EXPORT DVConfiguration */ unsigned long getQueryRetrieveMaxAssociations(); + /** returns the protocol family to be supported by the Query/Retrieve SCP + * as configured in section GENERAL/QUERY_RETRIEVE/PROTOCOL in the config file. + * @return send application path name or NULL if absent. + */ + T_ASC_ProtocolFamily getQueryRetrieveProtocolFamily(); + /** returns the database folder to be used for sending/receiving/browsing. * Value is taken from the section GENERAL/DATABASE/DIRECTORY * in the config file. If absent, a default value is returned. @@ -945,22 +959,22 @@ class DCMTK_DCMPSTAT_EXPORT DVConfiguration double getVOIPresetWindowWidth(const char *modality, Uint32 idx); /* TLS settings */ - - /** returns the directory in which TLS related files (certificates, keys, - * random data, Diffie-Hellman parameters etc.) are located. + + /** returns the directory in which TLS related files (certificates, keys, + * random data, Diffie-Hellman parameters etc.) are located. * @return TLS directory path, NULL if absent. */ const char *getTLSFolder(); - /** returns the directory in which certificates of the trusted - * Certification Authorities are located. + /** returns the directory in which certificates of the trusted + * Certification Authorities are located. * @return TLS CA Certificate directory path, NULL if absent. */ const char *getTLSCACertificateFolder(); /** returns the file format used for certificates, keys and Diffie-Hellman * parameters. OFTrue for PEM ("privacy enhanced mail") format, OFFalse for - * DER ("distinguished encoding rules") format. + * DER ("distinguished encoding rules") format. * @return OFTrue for PEM (default), OFFalse for DER. */ OFBool getTLSPEMFormat(); @@ -968,7 +982,7 @@ class DCMTK_DCMPSTAT_EXPORT DVConfiguration /* User login settings */ /** returns the directory in which user certificates and keys - * are located. + * are located. * @return User key/certificate directory path, NULL if absent. */ const char *getUserCertificateFolder(); @@ -992,7 +1006,7 @@ class DCMTK_DCMPSTAT_EXPORT DVConfiguration */ const char *getUserLogin(const char *userID); - /** returns the human readable name for the given user. + /** returns the human readable name for the given user. * If absent in the config file, returns NULL. * @param userID user ID as returned by getUserID() * @return name for the given user diff --git a/dcmpstat/include/dcmtk/dcmpstat/dvpsri.h b/dcmpstat/include/dcmtk/dcmpstat/dvpsri.h index 0faa4401..9e7abb24 100644 --- a/dcmpstat/include/dcmtk/dcmpstat/dvpsri.h +++ b/dcmpstat/include/dcmtk/dcmpstat/dvpsri.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1998-2012, OFFIS e.V. + * Copyright (C) 1998-2024, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -36,6 +36,32 @@ class DCMTK_DCMPSTAT_EXPORT DVPSReferencedImage { +public: + /** SOPClassUID Validation Mode + */ + enum E_ClassValidationMode + { + /** enforce the rule specified in DICOM Part 3, C.11.11, + * that the referenced SOP class must be same for all images + * referenced by a presentation state. + */ + CVM_standard, + + /** somewhat relax the rule that the referenced SOP class must + * be same for all images referenced by a presentation state + * by permitting instances of related "for presentation" and + * "for processing" SOP classes to be referenced from one + * presentation state. + */ + + CVM_accept_Presentation_and_Processing, + + /** ignore the rule that the referenced SOP class must + * be same for all images referenced by a presentation state + */ + CVM_accept_all + }; + public: /// default constructor DVPSReferencedImage(); @@ -170,4 +196,10 @@ private: }; +/** This flag defines the validation mode applied when reading presentation + * states. See definition of enum E_ClassValidationMode for details. + * The default is CVM_standard. + */ +extern DCMTK_DCMPSTAT_EXPORT OFGlobal dcmPresentationStateValidationMode; + #endif diff --git a/dcmpstat/libsrc/Makefile.dep b/dcmpstat/libsrc/Makefile.dep index 1602dfb2..f844e0a9 100644 --- a/dcmpstat/libsrc/Makefile.dep +++ b/dcmpstat/libsrc/Makefile.dep @@ -2966,6 +2966,7 @@ dvpspl2.o: dvpspl2.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dipixel.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimomod.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dibaslut.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimoopx.h \ ../../dcmimgle/include/dcmtk/dcmimgle/didispfn.h \ @@ -3070,6 +3071,7 @@ dvpspll.o: dvpspll.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ ../include/dcmtk/dcmpstat/dvpsibl.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dibaslut.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diutils.h \ ../../dcmimgle/include/dcmtk/dcmimgle/didefine.h \ @@ -3110,7 +3112,6 @@ dvpspll.o: dvpspll.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrst.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvruc.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrut.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpixel.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpobw.h \ ../../dcmdata/include/dcmtk/dcmdata/dcovlay.h \ @@ -3432,7 +3433,8 @@ dvpsri.o: dvpsri.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/oferror.h \ ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ - ../include/dcmtk/dcmpstat/dvpsdef.h + ../include/dcmtk/dcmpstat/dvpsdef.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcuid.h dvpsril.o: dvpsril.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmpstat/dvpsril.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ diff --git a/dcmpstat/libsrc/dviface.cc b/dcmpstat/libsrc/dviface.cc index 3b7cdc07..0a550352 100644 --- a/dcmpstat/libsrc/dviface.cc +++ b/dcmpstat/libsrc/dviface.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1998-2024, OFFIS e.V. + * Copyright (C) 1998-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -64,9 +64,7 @@ #include "dcmtk/dcmqrdb/dcmqrdbs.h" /* for DcmQueryRetrieveDatabaseStatus */ BEGIN_EXTERN_C -#ifdef HAVE_SYS_TYPES_H #include /* for fork */ -#endif #ifdef HAVE_SYS_WAIT_H #include /* for waitpid */ #endif @@ -76,9 +74,7 @@ BEGIN_EXTERN_C #ifdef HAVE_SYS_RESOURCE_H #include /* for wait3 */ #endif -#ifdef HAVE_SYS_STAT_H #include /* for stat, fstat */ -#endif #ifdef HAVE_SYS_UTIME_H #include /* for utime */ #endif @@ -2375,6 +2371,7 @@ OFCondition DVInterface::startQueryRetrieveServer() if (configPath.empty()) return EC_IllegalCall; OFString config_filename = getQueryRetrieveServerName(); + T_ASC_ProtocolFamily family = getQueryRetrieveProtocolFamily(); config_filename += ".cfg"; if (getQueryRetrieveAutoCreateConfigFile()) createQueryRetrieveServerConfigFile(config_filename.c_str()); @@ -2384,6 +2381,20 @@ OFCondition DVInterface::startQueryRetrieveServer() DVPSHelper::cleanChildren(); // clean up old child processes before creating new ones Sint32 timeout = getQueryRetrieveTimeout(); + const char *family_param = ""; + switch (family) + { + case ASC_AF_INET: + case ASC_AF_Default: + family_param = "--ipv4"; + break; + case ASC_AF_INET6: + family_param = "--ipv6"; + break; + case ASC_AF_UNSPEC: + family_param = "--ip-auto"; + break; + } #ifdef HAVE_FORK // Unix version - call fork() and execl() @@ -2402,12 +2413,11 @@ OFCondition DVInterface::startQueryRetrieveServer() { char str_timeout[20]; OFStandard::snprintf(str_timeout, sizeof(str_timeout), "%lu", OFstatic_cast(unsigned long, timeout)); - execl(server_application, server_application, "-c", config_filename.c_str(), "--allow-shutdown", - "--timeout", str_timeout, OFreinterpret_cast(char *, 0)); + execl(server_application, server_application, "-c", config_filename.c_str(), family_param, "--allow-shutdown", "--timeout", str_timeout, OFreinterpret_cast(char *, 0)); } else { - execl(server_application, server_application, "-c", config_filename.c_str(), "--allow-shutdown", OFreinterpret_cast(char *, 0)); + execl(server_application, server_application, "-c", config_filename.c_str(), family_param, "--allow-shutdown", OFreinterpret_cast(char *, 0)); } DCMPSTAT_ERROR("Unable to execute '" << server_application << "'"); @@ -2427,12 +2437,12 @@ OFCondition DVInterface::startQueryRetrieveServer() if (timeout > 0) { - OFStandard::snprintf(commandline, sizeof(commandline), "%s -c %s --allow-shutdown --timeout %lu", - server_application, config_filename.c_str(), (unsigned long) timeout); + OFStandard::snprintf(commandline, sizeof(commandline), "%s -c %s %s --allow-shutdown --timeout %lu", + server_application, config_filename.c_str(), family_param, (unsigned long) timeout); } else { - OFStandard::snprintf(commandline, sizeof(commandline), "%s -c %s --allow-shutdown", server_application, config_filename.c_str()); + OFStandard::snprintf(commandline, sizeof(commandline), "%s -c %s %s --allow-shutdown", server_application, config_filename.c_str(), family_param); } #ifdef DEBUG diff --git a/dcmpstat/libsrc/dvpscf.cc b/dcmpstat/libsrc/dvpscf.cc index 7f8cd142..9dc74dc0 100644 --- a/dcmpstat/libsrc/dvpscf.cc +++ b/dcmpstat/libsrc/dvpscf.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1998-2024, OFFIS e.V. + * Copyright (C) 1998-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -71,7 +71,7 @@ extern "C" int strncasecmp(const char *s1, const char *s2, size_t n); #define L0_DESCRIPTION "DESCRIPTION" #define L0_DETAILEDLOG "DETAILEDLOG" #define L0_DICOMNAME "DICOMNAME" -#define L0_DIFFIEHELLMANPARAMETERS "DIFFIEHELLMANPARAMETERS" +#define L0_DIFFIEHELLMANPARAMETERS "DIFFIEHELLMANPARAMETERS" #define L0_DIRECTORY "DIRECTORY" #define L0_DISABLENEWVRS "DISABLENEWVRS" #define L0_DISPLAYFORMAT "DISPLAYFORMAT" @@ -134,6 +134,7 @@ extern "C" int strncasecmp(const char *s1, const char *s2, size_t n); #define L0_USEPEMFORMAT "USEPEMFORMAT" #define L0_USERKEYDIRECTORY "USERKEYDIRECTORY" #define L0_USETLS "USETLS" +#define L0_VALIDATIONMODE "VALIDATIONMODE" #define L0_WIDTH "WIDTH" #define L1_APPLICATION "APPLICATION" #define L1_DATABASE "DATABASE" @@ -224,7 +225,7 @@ static int strCompare(const char *str1, const char *str2, size_t len) return _strnicmp(str1, str2, len); #else return strncasecmp(str1, str2, len); -#endif +#endif } @@ -562,6 +563,19 @@ unsigned long DVConfiguration::getQueryRetrieveMaxAssociations() return result; } +T_ASC_ProtocolFamily DVConfiguration::getQueryRetrieveProtocolFamily() +{ + const char *c = getConfigEntry(L2_GENERAL, L1_QUERY_RETRIEVE, L0_PROTOCOL); + T_ASC_ProtocolFamily result = ASC_AF_Default; + if (c) + { + if (strCompare(c, "AF_INET6", 8) == 0) result = ASC_AF_INET6; + else if (strCompare(c, "AF_INET", 7) == 0) result = ASC_AF_INET; + else if (strCompare(c, "AF_UNSPEC", 9) == 0) result = ASC_AF_UNSPEC; + } + return result; +} + const char *DVConfiguration::getDatabaseFolder() { const char *result = getConfigEntry(L2_GENERAL, L1_DATABASE, L0_DIRECTORY); @@ -1303,7 +1317,7 @@ const char *DVConfiguration::getTargetPrinterAnnotationDisplayFormatID(const cha copyValue(getConfigEntry(L2_COMMUNICATION, targetID, L0_ANNOTATION), 1, value); if (value.length()) return value.c_str(); else return NULL; } - + Uint16 DVConfiguration::getTargetPrinterAnnotationPosition(const char *targetID) { OFString value; @@ -1331,6 +1345,11 @@ OFBool DVConfiguration::getTLSPEMFormat() return getConfigBoolEntry(L2_GENERAL, L1_TLS, L0_USEPEMFORMAT, OFTrue); } +const char *DVConfiguration::getTargetValidationMode(const char *targetID) +{ + return getConfigEntry(L2_COMMUNICATION, targetID, L0_VALIDATIONMODE); +} + OFBool DVConfiguration::getTargetBitPreservingMode(const char *targetID) { return getConfigBoolEntry(L2_COMMUNICATION, targetID, L0_BITPRESERVINGMODE, OFFalse); diff --git a/dcmpstat/libsrc/dvpshlp.cc b/dcmpstat/libsrc/dvpshlp.cc index fd1057a1..097a4a90 100644 --- a/dcmpstat/libsrc/dvpshlp.cc +++ b/dcmpstat/libsrc/dvpshlp.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1998-2024, OFFIS e.V. + * Copyright (C) 1998-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -27,9 +27,7 @@ #include "dcmtk/dcmdata/dctk.h" BEGIN_EXTERN_C -#ifdef HAVE_SYS_TYPES_H #include /* for fork */ -#endif #ifdef HAVE_SYS_WAIT_H #include /* for waitpid */ #endif diff --git a/dcmpstat/libsrc/dvpsmsg.cc b/dcmpstat/libsrc/dvpsmsg.cc index 743c023b..ac0da5ad 100644 --- a/dcmpstat/libsrc/dvpsmsg.cc +++ b/dcmpstat/libsrc/dvpsmsg.cc @@ -279,6 +279,10 @@ void DVPSIPCClient::requestConnection() { if (connection) return; // connection already open + OFSockAddr server; + OFStandard::getAddressByHostname("localhost", AF_INET, server); + if (server.size() == 0) return; + #ifdef _WIN32 SOCKET s = socket(AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) return; @@ -286,8 +290,7 @@ void DVPSIPCClient::requestConnection() int s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return; #endif - OFSockAddr server; - OFStandard::getAddressByHostname("localhost", AF_INET, server); + server.setPort(OFstatic_cast(unsigned short, htons(OFstatic_cast(unsigned short, port)))); if (connect(s, server.getSockaddr(), server.size()) < 0) diff --git a/dcmpstat/libsrc/dvpsri.cc b/dcmpstat/libsrc/dvpsri.cc index 2cc9d11e..9f742c2a 100644 --- a/dcmpstat/libsrc/dvpsri.cc +++ b/dcmpstat/libsrc/dvpsri.cc @@ -25,10 +25,14 @@ #include "dcmtk/dcmdata/dcdeftag.h" #include "dcmtk/dcmdata/dcitem.h" #include "dcmtk/dcmpstat/dvpsdef.h" /* for constants and macros */ +#include "dcmtk/dcmdata/dcuid.h" #include "dcmtk/ofstd/ofstd.h" +OFGlobal dcmPresentationStateValidationMode(DVPSReferencedImage::CVM_standard); + /* --------------- class DVPSReferencedImage --------------- */ + DVPSReferencedImage::DVPSReferencedImage() : referencedSOPClassUID(DCM_ReferencedSOPClassUID) , referencedSOPInstanceUID(DCM_ReferencedSOPInstanceUID) @@ -58,11 +62,11 @@ OFCondition DVPSReferencedImage::read(DcmItem &dset) DcmStack stack; flushCache(); - + READ_FROM_DATASET(DcmUniqueIdentifier, EVR_UI, referencedSOPClassUID) READ_FROM_DATASET(DcmUniqueIdentifier, EVR_UI, referencedSOPInstanceUID) READ_FROM_DATASET(DcmIntegerString, EVR_IS, referencedFrameNumber) - + /* Now perform basic sanity checks */ if (referencedSOPClassUID.getLength() == 0) @@ -94,7 +98,7 @@ OFCondition DVPSReferencedImage::write(DcmItem &dset) { OFCondition result = EC_Normal; DcmElement *delem=NULL; - + ADD_TO_DATASET(DcmUniqueIdentifier, referencedSOPClassUID) ADD_TO_DATASET(DcmUniqueIdentifier, referencedSOPInstanceUID) if (referencedFrameNumber.getLength() >0) { ADD_TO_DATASET(DcmIntegerString, referencedFrameNumber) } @@ -104,19 +108,64 @@ OFCondition DVPSReferencedImage::write(DcmItem &dset) OFBool DVPSReferencedImage::validateSOPClassUID(OFString& sopclassuid) { - OFBool result = OFTrue; + E_ClassValidationMode classValidationMode = dcmPresentationStateValidationMode.get(); if (sopclassuid.empty()) referencedSOPClassUID.getOFString(sopclassuid, 0); - else + else { OFString currentUID; referencedSOPClassUID.getOFString(currentUID, 0); - if (currentUID != sopclassuid) + switch (classValidationMode) { - result = OFFalse; - DCMPSTAT_WARN("images of different SOP classes referenced in presentation state"); + // we accept all combinations of SOP classes + case CVM_accept_all: + if (currentUID != sopclassuid) + { + DCMPSTAT_INFO("images of different SOP classes referenced in presentation state"); + } + return OFTrue; + /* break; */ + + // we accept only related SOP classes + case CVM_accept_Presentation_and_Processing: + if (currentUID != sopclassuid) + { + if ( ( sopclassuid == UID_BreastProjectionXRayImageStorageForPresentation && currentUID == UID_BreastProjectionXRayImageStorageForProcessing ) + || ( sopclassuid == UID_BreastProjectionXRayImageStorageForProcessing && currentUID == UID_BreastProjectionXRayImageStorageForPresentation ) + || ( sopclassuid == UID_DigitalIntraOralXRayImageStorageForPresentation && currentUID == UID_DigitalIntraOralXRayImageStorageForProcessing ) + || ( sopclassuid == UID_DigitalIntraOralXRayImageStorageForProcessing && currentUID == UID_DigitalIntraOralXRayImageStorageForPresentation ) + || ( sopclassuid == UID_DigitalMammographyXRayImageStorageForPresentation && currentUID == UID_DigitalMammographyXRayImageStorageForProcessing ) + || ( sopclassuid == UID_DigitalMammographyXRayImageStorageForProcessing && currentUID == UID_DigitalMammographyXRayImageStorageForPresentation ) + || ( sopclassuid == UID_DigitalXRayImageStorageForPresentation && currentUID == UID_DigitalXRayImageStorageForProcessing ) + || ( sopclassuid == UID_DigitalXRayImageStorageForProcessing && currentUID == UID_DigitalXRayImageStorageForPresentation ) + || ( sopclassuid == UID_IntravascularOpticalCoherenceTomographyImageStorageForPresentation && currentUID == UID_IntravascularOpticalCoherenceTomographyImageStorageForProcessing ) + || ( sopclassuid == UID_IntravascularOpticalCoherenceTomographyImageStorageForProcessing && currentUID == UID_IntravascularOpticalCoherenceTomographyImageStorageForPresentation ) + || ( sopclassuid == UID_DICOS_DigitalXRayImageStorageForPresentation && currentUID == UID_DICOS_DigitalXRayImageStorageForProcessing ) + || ( sopclassuid == UID_DICOS_DigitalXRayImageStorageForProcessing && currentUID == UID_DICOS_DigitalXRayImageStorageForPresentation ) ) + { + DCMPSTAT_INFO("images of different (but related) SOP classes referenced in presentation state"); + return OFTrue; + } + else + { + DCMPSTAT_WARN("images of different SOP classes referenced in presentation state"); + return OFFalse; + } + } + return OFTrue; + /* break; */ + + // we accept only the same SOP classes, as required by the DICOM standard + case CVM_standard: + if (currentUID != sopclassuid) + { + DCMPSTAT_WARN("images of different SOP classes referenced in presentation state"); + return OFFalse; + } + else return OFTrue; + /* break; */ } } - return result; + return OFTrue; } void DVPSReferencedImage::setSOPClassUID(const char *uid) @@ -150,7 +199,7 @@ OFBool DVPSReferencedImage::isSOPInstanceUID(const char *uid) OFCondition DVPSReferencedImage::getImageReference( OFString& sopclassUID, - OFString& instanceUID, + OFString& instanceUID, OFString& frames) { OFCondition result = referencedSOPClassUID.getOFString(sopclassUID,0); @@ -204,7 +253,7 @@ OFBool DVPSReferencedImage::appliesToFrame(unsigned long frame) { val = (Sint32) frame; for (i=0; i /* we have to include this before sys/time.h on Solaris */ -#endif #ifdef HAVE_SYS_TIME_H #include /* for struct timeval on Linux */ #endif diff --git a/dcmqrdb/apps/dcmqrscp.cc b/dcmqrdb/apps/dcmqrscp.cc index b4ddc2b3..d546c0db 100644 --- a/dcmqrdb/apps/dcmqrscp.cc +++ b/dcmqrdb/apps/dcmqrscp.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1993-2024, OFFIS e.V. + * Copyright (C) 1993-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -25,9 +25,7 @@ BEGIN_EXTERN_C #ifdef HAVE_SYS_FILE_H #include #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_WAIT_H #include #endif @@ -131,7 +129,6 @@ main(int argc, char *argv[]) #endif OFCommandLine cmd; - cmd.setParamColumn(LONGCOL + SHORTCOL + 4); cmd.addParam("port", "tcp/ip port number to listen on\n(default: in config file)", OFCmdParam::PM_Optional); @@ -185,6 +182,11 @@ main(int argc, char *argv[]) #endif cmd.addGroup("network options:"); + cmd.addSubGroup("IP protocol version:"); + cmd.addOption("--ipv4", "-i4", "use IPv4 only (default)"); + cmd.addOption("--ipv6", "-i6", "use IPv6 only"); + cmd.addOption("--ip-auto", "-i0", "use IPv6/IPv4 dual stack"); + cmd.addSubGroup("association negotiation profiles from configuration file:"); cmd.addOption("--assoc-config-file", "-xf", 3, "[f]ilename, [i]n-prof, [o]ut-prof: string", "use profile i from f for incoming,\nand profile o from f for outgoing associations"); @@ -518,6 +520,13 @@ main(int argc, char *argv[]) if (cmd.findOption("--ignore")) options.ignoreStoreData_ = OFTrue; if (cmd.findOption("--uid-padding")) options.correctUIDPadding_ = OFTrue; + // set the IP protocol version + cmd.beginOptionBlock(); + if (cmd.findOption("--ipv4")) dcmIncomingProtocolFamily.set(ASC_AF_INET); + if (cmd.findOption("--ipv6")) dcmIncomingProtocolFamily.set(ASC_AF_INET6); + if (cmd.findOption("--ip-auto")) dcmIncomingProtocolFamily.set(ASC_AF_UNSPEC); + cmd.endOptionBlock(); + if (cmd.findOption("--assoc-config-file")) { // check conflicts with other command line options @@ -798,6 +807,7 @@ main(int argc, char *argv[]) // evaluate (most of) the TLS command line options (if we are compiling with OpenSSL) tlsOptions.parseArguments(app, cmd); + options.secureConnectionRequested_ = tlsOptions.secureConnectionRequested(); } /* print resource identifier */ diff --git a/dcmqrdb/docs/dcmqridx.man b/dcmqrdb/docs/dcmqridx.man index 7f4c6e0b..d74f0951 100644 --- a/dcmqrdb/docs/dcmqridx.man +++ b/dcmqrdb/docs/dcmqridx.man @@ -131,6 +131,6 @@ It is an error if no data dictionary can be loaded. \section dcmqridx_copyright COPYRIGHT -Copyright (C) 1993-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1993-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmqrdb/docs/dcmqrscp.man b/dcmqrdb/docs/dcmqrscp.man index cf820047..3c31b3a8 100644 --- a/dcmqrdb/docs/dcmqrscp.man +++ b/dcmqrdb/docs/dcmqrscp.man @@ -183,6 +183,17 @@ restriction of query/retrieve models: \subsection dcmqrscp_network_options network options \verbatim +IP protocol version: + + -i4 --ipv4 + use IPv4 only (default) + + -i6 --ipv6 + use IPv6 only + + -i0 --ip-auto + use IPv6/IPv4 dual stack + association negotiation profiles from configuration file: -xf --assoc-config-file @@ -894,6 +905,8 @@ ElectromyogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7 ElectrooculogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7.3 SleepElectroencephalogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7.4 BodyPositionWaveformStorage 1.2.840.10008.5.1.4.1.1.9.8.1 +WaveformPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.1 +WaveformAcquisitionPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.2 RETIRED_StandaloneModalityLUTStorage 1.2.840.10008.5.1.4.1.1.10 RETIRED_StandaloneVOILUTStorage 1.2.840.10008.5.1.4.1.1.11 GrayscaleSoftcopyPresentationStateStorage 1.2.840.10008.5.1.4.1.1.11.1 @@ -1041,6 +1054,7 @@ DICONDE_EddyCurrentImageStorage 1.2.840.10008.5.1.4.1.1.601 DICONDE_EddyCurrentMultiframeImageStorage 1.2.840.10008.5.1.4.1.1.601.2 DICONDE_ThermographyImageStorage 1.2.840.10008.5.1.4.1.1.601.3 DICONDE_ThermographyMultiFrameImageStorage 1.2.840.10008.5.1.4.1.1.601.4 +DICONDE_UltrasoundWaveformStorage 1.2.840.10008.5.1.4.1.1.601.5 DRAFT_RTBeamsDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.1 RTBeamsDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.7 RTBrachyApplicationSetupDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.10 @@ -1114,7 +1128,7 @@ Query/Retrieve Level: PATIENT (or STUDY for the Study Root Q/R model) (0010,0040) PatientSex (0010,1000) OtherPatientIDs (retired) (0010,1001) OtherPatientNames -(0010,2160) EthnicGroup +(0010,2160) EthnicGroup (retired) (0010,4000) PatientComments \endverbatim @@ -1135,7 +1149,7 @@ Query/Retrieve Level: STUDY (0010,21B0) AdditionalPatientHistory (0020,000D) StudyInstanceUID (0020,0010) StudyID -(0020,1070) RETIRED_OtherStudyNumbers +(0020,1070) OtherStudyNumbers (retired) \endverbatim Query/Retrieve Level: SERIES @@ -1248,6 +1262,6 @@ profiles \section dcmqrscp_copyright COPYRIGHT -Copyright (C) 1993-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1993-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmqrdb/docs/dcmqrti.man b/dcmqrdb/docs/dcmqrti.man index 23fa203f..96182245 100644 --- a/dcmqrdb/docs/dcmqrti.man +++ b/dcmqrdb/docs/dcmqrti.man @@ -443,6 +443,6 @@ It is an error if no data dictionary can be loaded. \section dcmqrti_copyright COPYRIGHT -Copyright (C) 1993-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1993-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmqrdb/etc/dcmqrprf.cfg b/dcmqrdb/etc/dcmqrprf.cfg index 8c5a72b2..2f3d9626 100644 --- a/dcmqrdb/etc/dcmqrprf.cfg +++ b/dcmqrdb/etc/dcmqrprf.cfg @@ -1,5 +1,5 @@ # -# Copyright (C) 2017-2024, OFFIS e.V. +# Copyright (C) 2017-2025, OFFIS e.V. # All rights reserved. See COPYRIGHT file for details. # # This software and supporting documentation were developed by @@ -44,6 +44,9 @@ TransferSyntax1 = DeflatedLittleEndianExplicit TransferSyntax2 = LocalEndianExplicit TransferSyntax3 = OppositeEndianExplicit TransferSyntax4 = LittleEndianImplicit +# +# The "Deflated Image Frame Compression" transfer syntax is for images. +# [UncompressedEncapsulatedOrZlib] TransferSyntax1 = DeflatedLittleEndianExplicit @@ -53,6 +56,8 @@ TransferSyntax4 = LittleEndianImplicit # # The retired "Big Endian Explicit" transfer syntax is not accepted. # +# The "Deflated Image Frame Compression" transfer syntax is for images. +# [JPEGBaseline] TransferSyntax1 = JPEGBaseline @@ -136,11 +141,12 @@ TransferSyntax44 = HighThroughputJPEG2000ImageCompression TransferSyntax45 = JPEGXLLossless TransferSyntax46 = JPEGXLJPEGRecompression TransferSyntax47 = JPEGXL -TransferSyntax48 = DeflatedLittleEndianExplicit -TransferSyntax49 = EncapsulatedUncompressedLittleEndianExplicit -TransferSyntax50 = LocalEndianExplicit -TransferSyntax51 = OppositeEndianExplicit -TransferSyntax52 = LittleEndianImplicit +TransferSyntax48 = DeflatedImageFrameCompression +TransferSyntax49 = DeflatedLittleEndianExplicit +TransferSyntax50 = EncapsulatedUncompressedLittleEndianExplicit +TransferSyntax51 = LocalEndianExplicit +TransferSyntax52 = OppositeEndianExplicit +TransferSyntax53 = LittleEndianImplicit # ============================================================================ [[PresentationContexts]] @@ -343,46 +349,49 @@ PresentationContext178 = TwelveLeadECGWaveformStorage\UncompressedOrZlib PresentationContext179 = VariableModalityLUTSoftcopyPresentationStateStorage\UncompressedOrZlib PresentationContext180 = VisualAcuityMeasurementsStorage\UncompressedOrZlib PresentationContext181 = VolumeRenderingVolumetricPresentationStateStorage\UncompressedOrZlib -PresentationContext182 = WaveformAnnotationSRStorage\UncompressedOrZlib -PresentationContext183 = XADefinedProcedureProtocolStorage\UncompressedOrZlib -PresentationContext184 = XAPerformedProcedureProtocolStorage\UncompressedOrZlib -PresentationContext185 = XAXRFGrayscaleSoftcopyPresentationStateStorage\UncompressedOrZlib -PresentationContext186 = XRayRadiationDoseSRStorage\UncompressedOrZlib +PresentationContext182 = WaveformAcquisitionPresentationStateStorage\UncompressedOrZlib +PresentationContext183 = WaveformAnnotationSRStorage\UncompressedOrZlib +PresentationContext184 = WaveformPresentationStateStorage\UncompressedOrZlib +PresentationContext185 = XADefinedProcedureProtocolStorage\UncompressedOrZlib +PresentationContext186 = XAPerformedProcedureProtocolStorage\UncompressedOrZlib +PresentationContext187 = XAXRFGrayscaleSoftcopyPresentationStateStorage\UncompressedOrZlib +PresentationContext188 = XRayRadiationDoseSRStorage\UncompressedOrZlib # # retired non-image SOP classes # -PresentationContext187 = RETIRED_StandaloneCurveStorage\UncompressedOrZlib -PresentationContext188 = RETIRED_StandaloneModalityLUTStorage\UncompressedOrZlib -PresentationContext189 = RETIRED_StandaloneOverlayStorage\UncompressedOrZlib -PresentationContext190 = RETIRED_StandalonePETCurveStorage\UncompressedOrZlib -PresentationContext191 = RETIRED_StandaloneVOILUTStorage\UncompressedOrZlib -PresentationContext192 = RETIRED_StoredPrintStorage\UncompressedOrZlib +PresentationContext189 = RETIRED_StandaloneCurveStorage\UncompressedOrZlib +PresentationContext190 = RETIRED_StandaloneModalityLUTStorage\UncompressedOrZlib +PresentationContext191 = RETIRED_StandaloneOverlayStorage\UncompressedOrZlib +PresentationContext192 = RETIRED_StandalonePETCurveStorage\UncompressedOrZlib +PresentationContext193 = RETIRED_StandaloneVOILUTStorage\UncompressedOrZlib +PresentationContext194 = RETIRED_StoredPrintStorage\UncompressedOrZlib # # draft non-image SOP classes # -PresentationContext193 = DRAFT_RTBeamsDeliveryInstructionStorage\UncompressedOrZlib -PresentationContext194 = DRAFT_SRAudioStorage\UncompressedOrZlib -PresentationContext195 = DRAFT_SRComprehensiveStorage\UncompressedOrZlib -PresentationContext196 = DRAFT_SRDetailStorage\UncompressedOrZlib -PresentationContext197 = DRAFT_SRTextStorage\UncompressedOrZlib -PresentationContext198 = DRAFT_WaveformStorage\UncompressedOrZlib +PresentationContext195 = DRAFT_RTBeamsDeliveryInstructionStorage\UncompressedOrZlib +PresentationContext196 = DRAFT_SRAudioStorage\UncompressedOrZlib +PresentationContext197 = DRAFT_SRComprehensiveStorage\UncompressedOrZlib +PresentationContext198 = DRAFT_SRDetailStorage\UncompressedOrZlib +PresentationContext199 = DRAFT_SRTextStorage\UncompressedOrZlib +PresentationContext200 = DRAFT_WaveformStorage\UncompressedOrZlib # # DICOS Storage # -PresentationContext199 = DICOS_CTImageStorage\AnyTransferSyntax -PresentationContext200 = DICOS_DigitalXRayImageStorageForPresentation\AnyTransferSyntax -PresentationContext201 = DICOS_DigitalXRayImageStorageForProcessing\AnyTransferSyntax -PresentationContext202 = DICOS_2DAITStorage\AnyTransferSyntax -PresentationContext203 = DICOS_3DAITStorage\AnyTransferSyntax -PresentationContext204 = DICOS_QuadrupoleResonanceStorage\UncompressedOrZlib -PresentationContext205 = DICOS_ThreatDetectionReportStorage\UncompressedOrZlib +PresentationContext201 = DICOS_CTImageStorage\AnyTransferSyntax +PresentationContext202 = DICOS_DigitalXRayImageStorageForPresentation\AnyTransferSyntax +PresentationContext203 = DICOS_DigitalXRayImageStorageForProcessing\AnyTransferSyntax +PresentationContext204 = DICOS_2DAITStorage\AnyTransferSyntax +PresentationContext205 = DICOS_3DAITStorage\AnyTransferSyntax +PresentationContext206 = DICOS_QuadrupoleResonanceStorage\UncompressedOrZlib +PresentationContext207 = DICOS_ThreatDetectionReportStorage\UncompressedOrZlib # # DICONDE Storage # -PresentationContext206 = DICONDE_EddyCurrentImageStorage\AnyTransferSyntax -PresentationContext207 = DICONDE_EddyCurrentMultiframeImageStorage\AnyTransferSyntax -PresentationContext208 = DICONDE_ThermographyImageStorage\AnyTransferSyntax -PresentationContext209 = DICONDE_ThermographyMultiFrameImageStorage\AnyTransferSyntax +PresentationContext208 = DICONDE_EddyCurrentImageStorage\AnyTransferSyntax +PresentationContext209 = DICONDE_EddyCurrentMultiframeImageStorage\AnyTransferSyntax +PresentationContext210 = DICONDE_ThermographyImageStorage\AnyTransferSyntax +PresentationContext211 = DICONDE_ThermographyMultiFrameImageStorage\AnyTransferSyntax +PresentationContext212 = DICONDE_UltrasoundWaveformStorage\UncompressedOrZlib # ---------------------------------------------------------------------------- @@ -631,7 +640,9 @@ PresentationContext128 = VideoPhotographicImageStorage\MPEG2 # - VisualAcuityMeasurementsStorage # - VLWholeSlideMicroscopyImageStorage # - VolumeRenderingVolumetricPresentationStateStorage +# - WaveformAcquisitionPresentationStateStorage # - WaveformAnnotationSRStorage +# - WaveformPresentationStateStorage # - WideFieldOphthalmicPhotographyStereographicProjectionImageStorage # - WideFieldOphthalmicPhotography3DCoordinatesImageStorage # - XAPerformedProcedureProtocolStorage @@ -665,6 +676,7 @@ PresentationContext128 = VideoPhotographicImageStorage\MPEG2 # - DICONDE_EddyCurrentMultiframeImageStorage # - DICONDE_ThermographyImageStorage # - DICONDE_ThermographyMultiFrameImageStorage +# - DICONDE_UltrasoundWaveformStorage # ============================================================================ [[SCPSCURoleSelection]] @@ -851,34 +863,37 @@ Role177 = TwelveLeadECGWaveformStorage\BOTH Role178 = VariableModalityLUTSoftcopyPresentationStateStorage\BOTH Role179 = VisualAcuityMeasurementsStorage\BOTH Role180 = VolumeRenderingVolumetricPresentationStateStorage\BOTH -Role181 = WaveformAnnotationSRStorage\BOTH -Role182 = XADefinedProcedureProtocolStorage\BOTH -Role183 = XAPerformedProcedureProtocolStorage\BOTH -Role184 = XAXRFGrayscaleSoftcopyPresentationStateStorage\BOTH -Role185 = XRayRadiationDoseSRStorage\BOTH -Role186 = RETIRED_StandaloneCurveStorage\BOTH -Role187 = RETIRED_StandaloneModalityLUTStorage\BOTH -Role188 = RETIRED_StandaloneOverlayStorage\BOTH -Role189 = RETIRED_StandalonePETCurveStorage\BOTH -Role190 = RETIRED_StandaloneVOILUTStorage\BOTH -Role191 = RETIRED_StoredPrintStorage\BOTH -Role192 = DRAFT_RTBeamsDeliveryInstructionStorage\BOTH -Role193 = DRAFT_SRAudioStorage\BOTH -Role194 = DRAFT_SRComprehensiveStorage\BOTH -Role195 = DRAFT_SRDetailStorage\BOTH -Role196 = DRAFT_SRTextStorage\BOTH -Role197 = DRAFT_WaveformStorage\BOTH -Role198 = DICOS_CTImageStorage\BOTH -Role199 = DICOS_DigitalXRayImageStorageForPresentation\BOTH -Role200 = DICOS_DigitalXRayImageStorageForProcessing\BOTH -Role201 = DICOS_2DAITStorage\BOTH -Role202 = DICOS_3DAITStorage\BOTH -Role203 = DICOS_QuadrupoleResonanceStorage\BOTH -Role204 = DICOS_ThreatDetectionReportStorage\BOTH -Role205 = DICONDE_EddyCurrentImageStorage\BOTH -Role206 = DICONDE_EddyCurrentMultiframeImageStorage\BOTH -Role207 = DICONDE_ThermographyImageStorage\BOTH -Role208 = DICONDE_ThermographyMultiFrameImageStorage\BOTH +Role181 = WaveformAcquisitionPresentationStateStorage\BOTH +Role182 = WaveformAnnotationSRStorage\BOTH +Role183 = WaveformPresentationStateStorage\BOTH +Role184 = XADefinedProcedureProtocolStorage\BOTH +Role185 = XAPerformedProcedureProtocolStorage\BOTH +Role186 = XAXRFGrayscaleSoftcopyPresentationStateStorage\BOTH +Role187 = XRayRadiationDoseSRStorage\BOTH +Role188 = RETIRED_StandaloneCurveStorage\BOTH +Role189 = RETIRED_StandaloneModalityLUTStorage\BOTH +Role190 = RETIRED_StandaloneOverlayStorage\BOTH +Role191 = RETIRED_StandalonePETCurveStorage\BOTH +Role192 = RETIRED_StandaloneVOILUTStorage\BOTH +Role193 = RETIRED_StoredPrintStorage\BOTH +Role194 = DRAFT_RTBeamsDeliveryInstructionStorage\BOTH +Role195 = DRAFT_SRAudioStorage\BOTH +Role196 = DRAFT_SRComprehensiveStorage\BOTH +Role197 = DRAFT_SRDetailStorage\BOTH +Role198 = DRAFT_SRTextStorage\BOTH +Role199 = DRAFT_WaveformStorage\BOTH +Role200 = DICOS_CTImageStorage\BOTH +Role201 = DICOS_DigitalXRayImageStorageForPresentation\BOTH +Role202 = DICOS_DigitalXRayImageStorageForProcessing\BOTH +Role203 = DICOS_2DAITStorage\BOTH +Role204 = DICOS_3DAITStorage\BOTH +Role205 = DICOS_QuadrupoleResonanceStorage\BOTH +Role206 = DICOS_ThreatDetectionReportStorage\BOTH +Role207 = DICONDE_EddyCurrentImageStorage\BOTH +Role208 = DICONDE_EddyCurrentMultiframeImageStorage\BOTH +Role209 = DICONDE_ThermographyImageStorage\BOTH +Role210 = DICONDE_ThermographyMultiFrameImageStorage\BOTH +Role211 = DICONDE_UltrasoundWaveformStorage\BOTH # ============================================================================ [[Profiles]] diff --git a/dcmqrdb/include/dcmtk/dcmqrdb/dcmqrcbm.h b/dcmqrdb/include/dcmtk/dcmqrdb/dcmqrcbm.h index 538dae3c..b47292f0 100644 --- a/dcmqrdb/include/dcmtk/dcmqrdb/dcmqrcbm.h +++ b/dcmqrdb/include/dcmtk/dcmqrdb/dcmqrcbm.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1993-2018, OFFIS e.V. + * Copyright (C) 1993-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -46,6 +46,7 @@ public: * @param assoc pointer to DIMSE association * @param msgid DIMSE message ID * @param pr DIMSE priority + * @param secureConnect a flag indicating whether or not a secure connection was requested */ DcmQueryRetrieveMoveContext( DcmQueryRetrieveDatabaseHandle& handle, @@ -55,7 +56,8 @@ public: DIC_US priorstatus, T_ASC_Association *assoc, DIC_US msgid, - T_DIMSE_Priority pr) + T_DIMSE_Priority pr, + OFBool secureConnect) : dbHandle(handle) , options_(options) , associationConfiguration_(associationConfiguration) @@ -75,6 +77,7 @@ public: , nCompleted(0) , nFailed(0) , nWarning(0) + , secureConnection(secureConnect) { origAETitle[0] = '\0'; origHostName[0] = '\0'; @@ -183,6 +186,8 @@ private: /// number of completed sub-operations that causes warnings DIC_US nWarning; + /// a flag indicating whether or not a secure connection was requested + OFBool secureConnection; }; #endif diff --git a/dcmqrdb/include/dcmtk/dcmqrdb/dcmqropt.h b/dcmqrdb/include/dcmtk/dcmqrdb/dcmqropt.h index bc83777c..26dbdec5 100644 --- a/dcmqrdb/include/dcmtk/dcmqrdb/dcmqropt.h +++ b/dcmqrdb/include/dcmtk/dcmqrdb/dcmqropt.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1993-2017, OFFIS e.V. + * Copyright (C) 1993-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -168,6 +168,10 @@ public: /// profile name for outgoing association configuration OFString outgoingProfile; + + /// secure connection requested? + OFBool secureConnectionRequested_; + }; diff --git a/dcmqrdb/libsrc/dcmqrcbg.cc b/dcmqrdb/libsrc/dcmqrcbg.cc index fee635b4..ddd3305e 100644 --- a/dcmqrdb/libsrc/dcmqrcbg.cc +++ b/dcmqrdb/libsrc/dcmqrcbg.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1993-2021, OFFIS e.V. + * Copyright (C) 1993-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -33,9 +33,7 @@ #include "dcmtk/ofstd/ofstd.h" BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include /* needed on Solaris for O_RDONLY */ -#endif END_EXTERN_C static void getSubOpProgressCallback(void * /* callbackData */, @@ -176,9 +174,9 @@ OFCondition DcmQueryRetrieveGetContext::performGetSubOp(DIC_UI sopClass, DIC_UI /* shared lock image file */ int lockfd; #ifdef O_BINARY - lockfd = open(fname, O_RDONLY | O_BINARY, 0666); + lockfd = open(fname, O_RDONLY | O_BINARY); #else - lockfd = open(fname, O_RDONLY , 0666); + lockfd = open(fname, O_RDONLY); #endif if (lockfd < 0) { /* due to quota system the file could have been deleted */ diff --git a/dcmqrdb/libsrc/dcmqrcbm.cc b/dcmqrdb/libsrc/dcmqrcbm.cc index 9c954ae5..eb390344 100644 --- a/dcmqrdb/libsrc/dcmqrcbm.cc +++ b/dcmqrdb/libsrc/dcmqrcbm.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1993-2022, OFFIS e.V. + * Copyright (C) 1993-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -34,9 +34,7 @@ #include "dcmtk/ofstd/ofstd.h" BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include /* needed on Solaris for O_RDONLY */ -#endif END_EXTERN_C @@ -201,9 +199,9 @@ OFCondition DcmQueryRetrieveMoveContext::performMoveSubOp(DIC_UI sopClass, DIC_U /* shared lock image file */ int lockfd; #ifdef O_BINARY - lockfd = open(fname, O_RDONLY | O_BINARY, 0666); + lockfd = open(fname, O_RDONLY | O_BINARY); #else - lockfd = open(fname, O_RDONLY , 0666); + lockfd = open(fname, O_RDONLY); #endif if (lockfd < 0) { /* due to quota system the file could have been deleted */ @@ -317,6 +315,17 @@ OFCondition DcmQueryRetrieveMoveContext::buildSubAssociation(T_DIMSE_C_MoveRQ *r DCMQRDB_ERROR("moveSCP: Cannot create Association-params for sub-ops: " << DimseCondition::dump(temp_str, cond)); } } + + if (cond.good()) { + // use the same network protocol family for incoming and outgoing connections + ASC_setProtocolFamily(params, dcmIncomingProtocolFamily.get()); + + cond = ASC_setTransportLayerType(params, options_.secureConnectionRequested_); + if (cond.bad()) { + DCMQRDB_ERROR("moveSCP: Cannot create TLS transport layer for sub-ops: " << DimseCondition::dump(temp_str, cond)); + } + } + if (cond.good()) { OFStandard::snprintf(dstHostNamePlusPort, sizeof(DIC_NODENAME), "%s:%d", dstHostName, dstPortNumber); ASC_setPresentationAddresses(params, OFStandard::getHostName().c_str(), @@ -331,6 +340,12 @@ OFCondition DcmQueryRetrieveMoveContext::buildSubAssociation(T_DIMSE_C_MoveRQ *r if (cond.bad()) { DCMQRDB_ERROR(DimseCondition::dump(temp_str, cond)); } + else { + cond = ASC_setTransportLayerType(params, secureConnection); + if (cond.bad()) { + DCMQRDB_ERROR(DimseCondition::dump(temp_str, cond)); + } + } DCMQRDB_DEBUG("Request Parameters:" << OFendl << ASC_dumpParameters(temp_str, params, ASC_ASSOC_RQ)); } if (cond.good()) { @@ -352,6 +367,7 @@ OFCondition DcmQueryRetrieveMoveContext::buildSubAssociation(T_DIMSE_C_MoveRQ *r if (cond.good()) { assocStarted = OFTrue; } + return cond; } diff --git a/dcmqrdb/libsrc/dcmqrcnf.cc b/dcmqrdb/libsrc/dcmqrcnf.cc index 0a02f060..ef2afb12 100644 --- a/dcmqrdb/libsrc/dcmqrcnf.cc +++ b/dcmqrdb/libsrc/dcmqrcnf.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1993-2024, OFFIS e.V. + * Copyright (C) 1993-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -295,8 +295,6 @@ void DcmQueryRetrieveConfig::panic(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - -#if defined(HAVE_VSNPRINTF) && defined(HAVE_PROTOTYPE_VSNPRINTF) char buf[4096]; #ifdef HAVE_PROTOTYPE_STD__VSNPRINTF @@ -310,11 +308,6 @@ void DcmQueryRetrieveConfig::panic(const char *fmt, ...) buf[4095] = '\0'; DCMQRDB_ERROR("CONFIG Error: " << buf << "!"); -#else - fprintf(stderr, "CONFIG Error: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "!\n"); -#endif va_end(ap); } diff --git a/dcmqrdb/libsrc/dcmqrdbi.cc b/dcmqrdb/libsrc/dcmqrdbi.cc index 132f8a11..c91116a1 100644 --- a/dcmqrdb/libsrc/dcmqrdbi.cc +++ b/dcmqrdb/libsrc/dcmqrdbi.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1993-2024, OFFIS e.V. + * Copyright (C) 1993-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -23,12 +23,8 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ BEGIN_EXTERN_C -#ifdef HAVE_SYS_STAT_H #include -#endif -#ifdef HAVE_FCNTL_H #include -#endif #ifdef HAVE_SYS_PARAM_H #include #endif @@ -72,7 +68,7 @@ static const DB_FindAttr TbFindAttr [] = { DB_FindAttr( DCM_PatientBirthTime, PATIENT_LEVEL, OPTIONAL_KEY ), DB_FindAttr( DCM_RETIRED_OtherPatientIDs, PATIENT_LEVEL, OPTIONAL_KEY ), DB_FindAttr( DCM_OtherPatientNames, PATIENT_LEVEL, OPTIONAL_KEY ), - DB_FindAttr( DCM_EthnicGroup, PATIENT_LEVEL, OPTIONAL_KEY ), + DB_FindAttr( DCM_RETIRED_EthnicGroup, PATIENT_LEVEL, OPTIONAL_KEY ), DB_FindAttr( DCM_PatientComments, PATIENT_LEVEL, OPTIONAL_KEY ), DB_FindAttr( DCM_IssuerOfPatientID, PATIENT_LEVEL, OPTIONAL_KEY ), DB_FindAttr( DCM_StudyDate, STUDY_LEVEL, REQUIRED_KEY ), @@ -210,7 +206,7 @@ static void DB_IdxInitRecord (IdxRecord *idx, int linksOnly) idx -> param[RECORDIDX_OtherPatientNames]. XTag = DCM_OtherPatientNames ; idx -> param[RECORDIDX_OtherPatientNames]. ValueLength = PN_MAX_LENGTH ; idx -> OtherPatientNames[0] = '\0' ; - idx -> param[RECORDIDX_EthnicGroup]. XTag = DCM_EthnicGroup ; + idx -> param[RECORDIDX_EthnicGroup]. XTag = DCM_RETIRED_EthnicGroup ; idx -> param[RECORDIDX_EthnicGroup]. ValueLength = SH_MAX_LENGTH ; idx -> EthnicGroup[0] = '\0' ; idx -> param[RECORDIDX_StudyDate]. XTag = DCM_StudyDate ; @@ -1381,8 +1377,10 @@ OFCondition DcmQueryRetrieveIndexDatabaseHandle::startFindRequest( /* only char string type tags are supported at the moment */ char *s = NULL; dcelem->getString(s); + /* the available space is always elem.ValueLength+1 */ - OFStandard::strlcpy(elem.PValueField, s, elem.ValueLength+1); + if (s) OFStandard::strlcpy(elem.PValueField, s, elem.ValueLength+1); + else elem.PValueField[0]='\0'; } /** If element is the Query Level, store it in handle */ @@ -2066,8 +2064,10 @@ OFCondition DcmQueryRetrieveIndexDatabaseHandle::startMoveRequest( /* only char string type tags are supported at the moment */ char *s = NULL; dcelem->getString(s); + /* the available space is always elem.ValueLength+1 */ - OFStandard::strlcpy(elem.PValueField, s, elem.ValueLength+1); + if (s) OFStandard::strlcpy(elem.PValueField, s, elem.ValueLength+1); + else elem.PValueField[0]='\0'; } /** If element is the Query Level, store it in handle @@ -2359,9 +2359,9 @@ OFCondition DcmQueryRetrieveIndexDatabaseHandle::deleteImageFile(char* imgFile) #ifdef LOCK_IMAGE_FILES int lockfd; #ifdef O_BINARY - lockfd = open(imgFile, O_RDWR | O_BINARY, 0666); /* obtain file descriptor */ + lockfd = open(imgFile, O_RDWR | O_BINARY); /* obtain file descriptor */ #else - lockfd = open(imgFile, O_RDWR, 0666); /* obtain file descriptor */ + lockfd = open(imgFile, O_RDWR); /* obtain file descriptor */ #endif if (lockfd < 0) { DCMQRDB_WARN("DB ERROR: cannot open image file for deleting: " << imgFile); diff --git a/dcmqrdb/libsrc/dcmqropt.cc b/dcmqrdb/libsrc/dcmqropt.cc index 31ddffb8..877aedae 100644 --- a/dcmqrdb/libsrc/dcmqropt.cc +++ b/dcmqrdb/libsrc/dcmqropt.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1993-2021, OFFIS e.V. + * Copyright (C) 1993-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -71,6 +71,7 @@ DcmQueryRetrieveOptions::DcmQueryRetrieveOptions() , associationConfigFile() , incomingProfile() , outgoingProfile() +, secureConnectionRequested_(OFFalse) { } diff --git a/dcmqrdb/libsrc/dcmqrsrv.cc b/dcmqrdb/libsrc/dcmqrsrv.cc index 4c450c28..97453e97 100644 --- a/dcmqrdb/libsrc/dcmqrsrv.cc +++ b/dcmqrdb/libsrc/dcmqrsrv.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1993-2024, OFFIS e.V. + * Copyright (C) 1993-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -323,7 +323,8 @@ OFCondition DcmQueryRetrieveSCP::moveSCP(T_ASC_Association * assoc, T_DIMSE_C_Mo T_ASC_PresentationContextID presID, DcmQueryRetrieveDatabaseHandle& dbHandle) { OFCondition cond = EC_Normal; - DcmQueryRetrieveMoveContext context(dbHandle, options_, associationConfiguration_, config_, STATUS_Pending, assoc, request->MessageID, request->Priority); + DcmQueryRetrieveMoveContext context(dbHandle, options_, associationConfiguration_, config_, STATUS_Pending, assoc, request->MessageID, request->Priority, + tlsOptions_.secureConnectionRequested()); DIC_AE aeTitle; aeTitle[0] = '\0'; diff --git a/dcmqrdb/libsrc/dcmqrtis.cc b/dcmqrdb/libsrc/dcmqrtis.cc index 30f309f7..b0ecef83 100644 --- a/dcmqrdb/libsrc/dcmqrtis.cc +++ b/dcmqrdb/libsrc/dcmqrtis.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1993-2024, OFFIS e.V. + * Copyright (C) 1993-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -35,12 +35,8 @@ BEGIN_EXTERN_C -#ifdef HAVE_SYS_STAT_H #include /* needed for stat() */ -#endif -#ifdef HAVE_FCNTL_H #include /* needed on Solaris for O_RDONLY */ -#endif END_EXTERN_C /* ========================================== helper functions ======================================== */ @@ -779,9 +775,9 @@ OFBool DcmQueryRetrieveTelnetInitiator::TI_storeImage(char *sopClass, char *sopI /* shared lock image file */ int lockfd; #ifdef O_BINARY - lockfd = open(imgFile, O_RDONLY | O_BINARY, 0666); + lockfd = open(imgFile, O_RDONLY | O_BINARY); #else - lockfd = open(imgFile, O_RDONLY, 0666); + lockfd = open(imgFile, O_RDONLY); #endif if (lockfd < 0) { DCMQRDB_WARN("CTN has deleted image, giving up (no imgFile): " diff --git a/dcmrt/apps/CMakeLists.txt b/dcmrt/apps/CMakeLists.txt index ad990ea0..24b723d9 100644 --- a/dcmrt/apps/CMakeLists.txt +++ b/dcmrt/apps/CMakeLists.txt @@ -2,4 +2,4 @@ DCMTK_ADD_EXECUTABLE(drtdump drtdump.cc) # make sure executables are linked to the corresponding libraries -DCMTK_TARGET_LINK_MODULES(drtdump dcmrt dcmdata oflog ofstd) +DCMTK_TARGET_LINK_MODULES(drtdump dcmrt) diff --git a/dcmrt/docs/drtdump.man b/dcmrt/docs/drtdump.man index 0796f417..75fc4b92 100644 --- a/dcmrt/docs/drtdump.man +++ b/dcmrt/docs/drtdump.man @@ -181,7 +181,7 @@ It is an error if no data dictionary can be loaded. \section drtdump_copyright COPYRIGHT -Copyright (C) 2010-2024 by OFFIS e.V. and ICSMED AG, Escherweg 2, 26121 +Copyright (C) 2010-2025 by OFFIS e.V. and ICSMED AG, Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmrt/include/dcmtk/dcmrt/drttypes.h b/dcmrt/include/dcmtk/dcmrt/drttypes.h index a4e7c65d..a65b8f8f 100644 --- a/dcmrt/include/dcmtk/dcmrt/drttypes.h +++ b/dcmrt/include/dcmtk/dcmrt/drttypes.h @@ -1,7 +1,7 @@ /* * * Copyright (c) 2008-2012, OFFIS e.V. and ICSMED AG, Oldenburg, Germany - * Copyright (C) 2013-2022, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2013-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class DRTTypes @@ -50,7 +50,7 @@ * These error codes can be used in addition to the general purpose * codes defined in module dcmdata. */ -//@{ +///@{ /// error: a value is invalid according to the standard extern DCMTK_DCMRT_EXPORT const OFConditionConst RT_EC_InvalidValue; @@ -61,7 +61,7 @@ extern DCMTK_DCMRT_EXPORT const OFConditionConst RT_EC_UnsupportedValue; /// error: the object is invalid, see isValid() method in IOD class extern DCMTK_DCMRT_EXPORT const OFConditionConst RT_EC_InvalidObject; -//@} +///@} // global definitions for logging mechanism provided by the oflog module diff --git a/dcmrt/libsrc/drtdose.cc b/dcmrt/libsrc/drtdose.cc index a2a7b08e..f2829f52 100644 --- a/dcmrt/libsrc/drtdose.cc +++ b/dcmrt/libsrc/drtdose.cc @@ -34,7 +34,7 @@ DRTDoseIOD::DRTDoseIOD() PatientBirthTime(DCM_PatientBirthTime), OtherPatientIDsSequence(), OtherPatientNames(DCM_OtherPatientNames), - EthnicGroup(DCM_EthnicGroup), + EthnicGroup(DCM_RETIRED_EthnicGroup), PatientComments(DCM_PatientComments), PatientSpeciesDescription(DCM_PatientSpeciesDescription), PatientSpeciesCodeSequence(), diff --git a/dcmrt/libsrc/drtimage.cc b/dcmrt/libsrc/drtimage.cc index 62478c89..14ca2020 100644 --- a/dcmrt/libsrc/drtimage.cc +++ b/dcmrt/libsrc/drtimage.cc @@ -34,7 +34,7 @@ DRTImageIOD::DRTImageIOD() PatientBirthTime(DCM_PatientBirthTime), OtherPatientIDsSequence(), OtherPatientNames(DCM_OtherPatientNames), - EthnicGroup(DCM_EthnicGroup), + EthnicGroup(DCM_RETIRED_EthnicGroup), PatientComments(DCM_PatientComments), PatientSpeciesDescription(DCM_PatientSpeciesDescription), PatientSpeciesCodeSequence(), diff --git a/dcmrt/libsrc/drtionpl.cc b/dcmrt/libsrc/drtionpl.cc index 4503ddad..c723c665 100644 --- a/dcmrt/libsrc/drtionpl.cc +++ b/dcmrt/libsrc/drtionpl.cc @@ -34,7 +34,7 @@ DRTIonPlanIOD::DRTIonPlanIOD() PatientBirthTime(DCM_PatientBirthTime), OtherPatientIDsSequence(), OtherPatientNames(DCM_OtherPatientNames), - EthnicGroup(DCM_EthnicGroup), + EthnicGroup(DCM_RETIRED_EthnicGroup), PatientComments(DCM_PatientComments), PatientSpeciesDescription(DCM_PatientSpeciesDescription), PatientSpeciesCodeSequence(), diff --git a/dcmrt/libsrc/drtiontr.cc b/dcmrt/libsrc/drtiontr.cc index 7ff9c56e..eb4e6e4a 100644 --- a/dcmrt/libsrc/drtiontr.cc +++ b/dcmrt/libsrc/drtiontr.cc @@ -34,7 +34,7 @@ DRTIonBeamsTreatmentRecordIOD::DRTIonBeamsTreatmentRecordIOD() PatientBirthTime(DCM_PatientBirthTime), OtherPatientIDsSequence(), OtherPatientNames(DCM_OtherPatientNames), - EthnicGroup(DCM_EthnicGroup), + EthnicGroup(DCM_RETIRED_EthnicGroup), PatientComments(DCM_PatientComments), PatientSpeciesDescription(DCM_PatientSpeciesDescription), PatientSpeciesCodeSequence(), diff --git a/dcmrt/libsrc/drtplan.cc b/dcmrt/libsrc/drtplan.cc index 1c47f8fd..ac44857e 100644 --- a/dcmrt/libsrc/drtplan.cc +++ b/dcmrt/libsrc/drtplan.cc @@ -34,7 +34,7 @@ DRTPlanIOD::DRTPlanIOD() PatientBirthTime(DCM_PatientBirthTime), OtherPatientIDsSequence(), OtherPatientNames(DCM_OtherPatientNames), - EthnicGroup(DCM_EthnicGroup), + EthnicGroup(DCM_RETIRED_EthnicGroup), PatientComments(DCM_PatientComments), PatientSpeciesDescription(DCM_PatientSpeciesDescription), PatientSpeciesCodeSequence(), diff --git a/dcmrt/libsrc/drtstrct.cc b/dcmrt/libsrc/drtstrct.cc index 6670863f..30897812 100644 --- a/dcmrt/libsrc/drtstrct.cc +++ b/dcmrt/libsrc/drtstrct.cc @@ -1,7 +1,7 @@ /* * * Copyright (C) 2008-2012, OFFIS e.V. and ICSMED AG, Oldenburg, Germany - * Copyright (C) 2013-2023, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2013-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class DRTStructureSetIOD @@ -34,7 +34,7 @@ DRTStructureSetIOD::DRTStructureSetIOD() PatientBirthTime(DCM_PatientBirthTime), OtherPatientIDsSequence(), OtherPatientNames(DCM_OtherPatientNames), - EthnicGroup(DCM_EthnicGroup), + EthnicGroup(DCM_RETIRED_EthnicGroup), PatientComments(DCM_PatientComments), PatientSpeciesDescription(DCM_PatientSpeciesDescription), PatientSpeciesCodeSequence(), diff --git a/dcmrt/libsrc/drttreat.cc b/dcmrt/libsrc/drttreat.cc index ea37dce6..99982848 100644 --- a/dcmrt/libsrc/drttreat.cc +++ b/dcmrt/libsrc/drttreat.cc @@ -34,7 +34,7 @@ DRTTreatmentSummaryRecordIOD::DRTTreatmentSummaryRecordIOD() PatientBirthTime(DCM_PatientBirthTime), OtherPatientIDsSequence(), OtherPatientNames(DCM_OtherPatientNames), - EthnicGroup(DCM_EthnicGroup), + EthnicGroup(DCM_RETIRED_EthnicGroup), PatientComments(DCM_PatientComments), PatientSpeciesDescription(DCM_PatientSpeciesDescription), PatientSpeciesCodeSequence(), diff --git a/dcmrt/tests/CMakeLists.txt b/dcmrt/tests/CMakeLists.txt index dc4e0d00..d06b0754 100644 --- a/dcmrt/tests/CMakeLists.txt +++ b/dcmrt/tests/CMakeLists.txt @@ -7,8 +7,8 @@ DCMTK_ADD_TEST_EXECUTABLE(dcmrt_tests ) # make sure executables are linked to the corresponding libraries -DCMTK_TARGET_LINK_MODULES(drttest dcmrt dcmdata oflog ofstd) -DCMTK_TARGET_LINK_MODULES(dcmrt_tests dcmrt dcmdata oflog ofstd) +DCMTK_TARGET_LINK_MODULES(drttest dcmrt) +DCMTK_TARGET_LINK_MODULES(dcmrt_tests dcmrt) # This macro parses tests.cc and registers all tests DCMTK_ADD_TESTS(dcmrt) diff --git a/dcmseg/Makefile.in b/dcmseg/Makefile.in index f1c79ee8..b2be6158 100644 --- a/dcmseg/Makefile.in +++ b/dcmseg/Makefile.in @@ -36,6 +36,7 @@ include-all: libsrc-all: include-all (cd libsrc && $(MAKE) ARCH="$(ARCH)" all) + tests-all: libsrc-all (cd tests && $(MAKE) ARCH="$(ARCH)" all) @@ -67,18 +68,12 @@ clean: (cd include && $(MAKE) clean) (cd libsrc && $(MAKE) clean) (cd tests && $(MAKE) clean) - (cd docs && $(MAKE) clean) - (cd data && $(MAKE) clean) - (cd etc && $(MAKE) clean) rm -f $(TRASH) distclean: (cd include && $(MAKE) distclean) (cd libsrc && $(MAKE) distclean) (cd tests && $(MAKE) distclean) - (cd docs && $(MAKE) distclean) - (cd data && $(MAKE) distclean) - (cd etc && $(MAKE) distclean) rm -f $(DISTTRASH) dependencies: diff --git a/dcmseg/include/dcmtk/dcmseg/overlaputil.h b/dcmseg/include/dcmtk/dcmseg/overlaputil.h new file mode 100644 index 00000000..54a90097 --- /dev/null +++ b/dcmseg/include/dcmtk/dcmseg/overlaputil.h @@ -0,0 +1,379 @@ +/* + * + * Copyright (C) 2023-2025, Open Connections GmbH + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmqi + * + * Author: Michael Onken + * + * Purpose: Interface of class OverlapUtil + * + */ + +#ifndef OVERLAPUTIL_H +#define OVERLAPUTIL_H + +#include "dcmtk/dcmiod/iodtypes.h" +#include "dcmtk/ofstd/ofcond.h" +#include "dcmtk/ofstd/oftypes.h" +#include "dcmtk/ofstd/ofvector.h" + +class DcmSegmentation; + +/** Class that analyzes the frame-based segment structures of a given segmentation object. + * It provides the following main functionality: + * - Grouping of "physical" frames by their position in space (called "logical frames"), i.e. + * frames that are at the same position in space are grouped together. + * - For every logical frame (i.e. position in space), it lists the segments found at this + * position together with their respective physical frame number + * - Return physical frame numbers for a given segment number + * - Building an overlap matrix that stores for each arbitrary segment pair whether + * they overlap or not. + * - Return groups of segments, that no two overlapping segments will be in the same group. + * This will not necessarily return the optimal solution, but a solution that should be good enough. + * For the method used see getNonOverlappingSegments(). + */ +class OverlapUtil +{ +public: + /// Image Position Patient tuple (x,y,z) + typedef OFVector ImagePosition; // might be defined in respective functional group + + /// Physical Frame number with its respective position + struct FramePositionAndNumber + { + /** Default constructor required for vector initialization + */ + FramePositionAndNumber() + : m_position() + , m_frameNumber(0) + { + } + /** Constructor + * @param pos Image position + * @param num Physical frame number + */ + FramePositionAndNumber(const ImagePosition& pos, const Uint32& num) + : m_position(pos) + , m_frameNumber(num) + { + } + /// Frame position in space + ImagePosition m_position; + /// Physical frame number (number of frame in DICOM object) + Uint32 m_frameNumber; + }; + + /// Physical Frame number with its respective position + typedef OFVector FramePositions; + + /// Logical Frame, represented and defined by various physical frames (numbers) at the same position + typedef OFVector LogicalFrame; + + /// All distinct positions and for each position the physical frame numbers that can be found at it + typedef OFVector DistinctFramePositions; + + /// Lists frames for each segment where segment with index i is represented by the vector at index i, + /// and index 0 is unused. I.e. index i is segment number, value is vector of physical frame numbers. + typedef OFVector > FramesForSegment; + + /// Implements comparision operator to be used for sorting of frame positions, + /// making the sorting order depend on the coordinate given in the constructor + struct ComparePositions + { + /** Constructor, used to configure coordinate position to be used for sorting + * @param c Coordinate position to be used for sorting + */ + ComparePositions(size_t c) + : m_coordinate(c) + { + } + + /** Compare two frames + * @param a First frame to compare + * @param b Second frame to compare + * @return Returns true if a is less than b based on the coordinate used for sorting + */ + bool operator()(const FramePositionAndNumber& a, const FramePositionAndNumber& b) const + { + return a.m_position[m_coordinate] < b.m_position[m_coordinate]; + } + /// Coordinate position (0-2, i.e. x,x,z) to be used for sorting + size_t m_coordinate; + }; + + /// Matrix of N x N segment numbers, where N is the number of segments. + /// Value is 1 at x,y if x and y overlap, 0 if they don't overlap, and -1 if not initialized. + typedef OFVector > OverlapMatrix; + + /// Group of non-overlapping segments (each represented by its segment number) + typedef OFVector > SegmentGroups; + + /** Represents a segment number and a logical frame number it is found at + */ + struct SegNumAndFrameNum + { + /** Constructor + * @param s Segment number + * @param f Logical frame number + */ + SegNumAndFrameNum(const Uint16 s, const Uint32 f) + : m_segmentNumber(s) + , m_frameNumber(f) + { + } + /** Default constructor + */ + SegNumAndFrameNum() + : m_segmentNumber(0) + , m_frameNumber(0) + { + } + + /// Segment number as used in DICOM segmentation object (1-n) + Uint16 m_segmentNumber; + /// Logical frame number (number of frame in DistinctFramePositions vector) + Uint32 m_frameNumber; + }; + + /// Segments and their phyiscal frame number (inner set), grouped by their + /// respective logical frame number (outer vector) .The inner vector is not + // sorted by segment number but will uniquely contain each segment only once. + // A std::set would be more appropriate, but since this is not supported by all + // compilers used for DCMTK, we use a vector and check for duplicates manually. + typedef OFVector > SegmentsByPosition; + + // ------------------------------------------ Methods ------------------------------------------ + + /** Constructor. Use setSegmentationObject() to set the segmentation object to work with. + */ + OverlapUtil(); + + /** Destructor */ + ~OverlapUtil(); + + /** Set the segmentation object to work with and clears all old data. + * @param seg The segmentation object to work with (not owned by this class) + */ + void setSegmentationObject(DcmSegmentation* seg); + + /** Clears all internal data (except segmentation object reference). + * This should be called whenever the input data (i.e. the underlying) + * DICOM segmentation object changes, before calling any other method. + */ + void clear(); + + /** Get all distinct frame positions and the physical frame numbers at this position + * @param result Resulting vector of distinct frame positions + * @return EC_Normal if successful, error otherwise + */ + OFCondition getFramesByPosition(DistinctFramePositions& result); + + /** Get all segments and their physical frame number, grouped by their respective logical frame number + * @param result Resulting vector of segments grouped by logical frame number + * @return EC_Normal if successful, error otherwise + */ + OFCondition getSegmentsByPosition(SegmentsByPosition& result); + + /** Get phyiscal frames for a specific segment by its segment number + * @param segmentNumber Segment number to get frames for (1..n) + * @param frames Resulting vector of physical frame numbers (first frame is frame 0) + * @return EC_Normal if successful, error otherwise + */ + OFCondition getFramesForSegment(const Uint32 segmentNumber, OFVector& frames); + + /** Returns computed overlap matrix + * @param matrix Resulting overlap matrix + * @return EC_Normal if successful, error otherwise + */ + OFCondition getOverlapMatrix(OverlapMatrix& matrix); + + /** Returns segments grouped together in a way, that no two overlapping + * segments will be in the same group. This method does not necessarily + * returns the optimal solution, but a solution that should be good enough. + * It is guaranteed, that segments in the same group don't overlap. + * + * It is based on the idea of a greedy algorithm that creates a first group + * containing the first segment. Then it goes to the next segment, checks whether + * it fits into the first group with no overlaps (easily checked in overlap matrix) + * and inserts it into that group if no overlaps exists. Otherwise, it creates a + * new group and continues with the next segment (trying to insert it into + * the first group, then second group, otherwise creates third group, and so on). + * @param segmentGroups Resulting vector of segment groups, each listing the + * segment numbers that are in that group + * @return EC_Normal if successful, error otherwise + */ + OFCondition getNonOverlappingSegments(SegmentGroups& segmentGroups); + + /** Prints segments by their position in space + * @param ss The stream to dump to + */ + void printSegmentsByPosition(OFStringStream& ss); + + /** Prints segment overlap matrix to given stream + * @param ss The stream to dump to + */ + void printOverlapMatrix(OFStringStream& ss); + + /** Prints groups of non-overlapping segments (identified by their numbers) + * to given stream + * @param ss The stream to dump to + */ + void printNonOverlappingSegments(OFStringStream& ss); + + /** Return whether there are at least two segments, that overlap each other. + * The overlap matrix is used (and computed if not already done) for this purpose. + * @return True if overlapping segments exist, false otherwise + */ + OFBool hasOverlappingSegments(); + + /** Returns the absolute value of a floating-point number + * @param value The input value + * @return The absolute value of the input + */ + static Float64 fabs(const Float64 value); + +protected: + + + /** Group physical frame positions into logical positions. This is done by sorting + * frames after *that* position coordinate that in its mean position difference is + * larger than slice thickness * 0.9. Then those frames that are close enough to + * each other (i.e. distance is smaller than slice thickness * 0.01), end up at the + * same logical position (considered a "logical frame") + * TODO: This should probably not use mean values for the coordinates + * since in some cases, the mean difference in a slice coordinate might be close to 0 + * if many frames are at the same position. Instead, the maximum difference, variance or + * something else could be used? + * @return EC_Normal if successful, error otherwise + */ + OFCondition groupFramesByLogicalPosition(); + + /** Builds the overlap matrix, if not already done. + * @return EC_Normal if successful or already existant, error otherwise + */ + OFCondition buildOverlapMatrix(); + + /** Checks if frames are parallel, i.e. if DICOM Image Position Patient is present and + * all frames are parallel to each other (i.e. found in the shared functional group) + * @return EC_Normal if parallel, SG_EC_FramesNotParallel if image orientation is not shared, + * error otherwise + */ + OFCondition ensureFramesAreParallel(); + + /** Groups all physical frames by their position. This also works if the physical frames + * have slightly different positions, i.e. if they are not exactly the same and are only + * "close enough" to be considered the same. Right now, the maximum distance treated equal + * is if distance is smaller than slice thickness * 0.01 (i.e. 1% of slice thickness). + * Only performs the computation, if not done before. + * @return EC_Normal if successful, error otherwise + */ + OFCondition groupFramesByPosition(); + + /** Checks whether the given two frames overlap + * @param f1 Frame 1, provided by its physical frame number + * @param f2 Frame 2, provided by its physical frame number + * @param overlap Resulting overlap (overlaps if OFTrue, otherwise not) + * @return EC_Normal if successful, error otherwise + */ + OFCondition checkFramesOverlap(const Uint32& f1, const Uint32& f2, OFBool& overlap); + + /** Checks whether the given two frames overlap by using comparing their pixel data + * by bitwise "and". This is very efficient, however, only works and is called (right now), + * if row*cols % 8 = 0, so we can easily extract frames as binary bitsets without unpacking them. + * TODO: Check whether this can be easily extended to other cases as well. + * @param f1 Frame 1, provided by its physical frame number + * @param f2 Frame 2, provided by its physical frame number + * @param f1_data Pixel data of frame 1 + * @param f2_data Pixel data of frame 2 + * @param rows Number of rows of the frame(s), not used right now + * @param cols Number of columns of the frame(s), not used right now + * @param overlap Resulting overlap (overlaps if OFTrue, otherwise not) + * @return EC_Normal if successful, error otherwise + */ + static OFCondition checkFramesOverlapBinary(const Uint32& f1, + const Uint32& f2, + const DcmIODTypes::Frame* f1_data, + const DcmIODTypes::Frame* f2_data, + const Uint16& /* rows */, + const Uint16& /* cols */, + OFBool& overlap); + + /** Checks whether the given two frames overlap by using comparing their pixel data + * after unpacking, i.e. expanding every bit to a byte, and then comparing whether the two + * related bytes of each frame are both non-zero. This is less efficient than checkFramesOverlapBinary(), + * @param f1 Frame 1, provided by its physical frame number + * @param f2 Frame 2, provided by its physical frame number + * @param f1_data Pixel data of frame 1 + * @param f2_data Pixel data of frame 2 + * @param rows Number of rows of the frame(s) + * @param cols Number of columns of the frame(s) + * @param overlap Resulting overlap (overlaps if OFTrue, otherwise not) + * @return EC_Normal if successful, error otherwise + */ + static OFCondition checkFramesOverlapUnpacked(const Uint32& f1, + const Uint32& f2, + const DcmIODTypes::Frame* f1_data, + const DcmIODTypes::Frame* f2_data, + const Uint16& rows, + const Uint16 cols, + OFBool& overlap); + + /** Return the most relevant (changing) coordinate, computed by multiplying + * x and y vectors of the image orientation and selecting the coordinate + * with the largest absolute value. + * @param imageOrientation Image orientation patient (3 coordinates for x vector, + * 3 coordinates for y vector ) + * @return 0 if x, 1 if y, 2 if z, 3 if not determinable + */ + static Uint8 identifyChangingCoordinate(const OFVector& imageOrientation); + +private: + /// Image Orientation Patient + OFVector m_imageOrientation; + + /// Phyiscal frames with their respective positions (IPP) + FramePositions m_framePositions; + + /// Outer vector with one entry per segment. Index is the DICOM segment + /// number where segment 1 goes to index 0, segment 2 to index 1, and so on. + /// Inner vector contains the physical frame numbers that represent the + /// segment. + FramesForSegment m_framesForSegment; + + /// Logical frames, ie. physical frames with the same position are + /// grouped together to a logical frame. For every logical frame, we + /// store the related physical frame numbers. The logical frame number + /// is implicitly given by the index in the vector. + DistinctFramePositions m_logicalFramePositions; + + /// Stores for each logical frame a collection of (paired) segment and + /// physical frame number, that exists at that position. + SegmentsByPosition m_segmentsByPosition; + + /// Matrix that stores for each segment pair whether they overlap or not. + /// I.e. Matrix has size N x N, where N is the number of segments. + /// The diagonal is always 0 (no overlap), i.e. a segment never overlaps with itself. + /// If there is an overlap, the value is 1. + /// If the field is not initialized, the value is -1. + OverlapMatrix m_segmentOverlapMatrix; + + //// Groups of segments that do not overlap with each other + SegmentGroups m_nonOverlappingSegments; + + /// Reference to segmentation object to work with + /// Must be freed outside this class. + DcmSegmentation* m_seg; +}; + +#endif // OVERLAPUTIL_H + diff --git a/dcmseg/include/dcmtk/dcmseg/segdoc.h b/dcmseg/include/dcmtk/dcmseg/segdoc.h index 5a75e1e4..c62aae49 100644 --- a/dcmseg/include/dcmtk/dcmseg/segdoc.h +++ b/dcmseg/include/dcmtk/dcmseg/segdoc.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -28,29 +28,38 @@ #include "dcmtk/dcmfg/concatenationcreator.h" // for writing concatenations #include "dcmtk/dcmfg/concatenationloader.h" // for loading concatenations #include "dcmtk/dcmfg/fginterface.h" // for multi-frame functional group interface +#include "dcmtk/dcmfg/fgseg.h" // for FGSegmentation class #include "dcmtk/dcmiod/iodimage.h" // common image IOD attribute access -#include "dcmtk/dcmiod/iodmacro.h" -#include "dcmtk/dcmiod/modenhequipment.h" // for enhanced general equipment module -#include "dcmtk/dcmiod/modimagepixel.h" +#include "dcmtk/dcmiod/iodmacro.h" // various macros +#include "dcmtk/dcmiod/modenhequipment.h" // for enhanced general equipment module +#include "dcmtk/dcmiod/modimagepixel.h" // for image pixel module #include "dcmtk/dcmiod/modmultiframedimension.h" // for multi-frame dimension module +#include "dcmtk/dcmiod/modiccprofile.h" // for ICC profile module #include "dcmtk/dcmiod/modmultiframefg.h" // for multi-frame functional group module #include "dcmtk/dcmiod/modsegmentationseries.h" // for segmentation series module -#include "dcmtk/dcmseg/segdef.h" +#include "dcmtk/dcmiod/modpalettecolorlut.h" // for palette color LUT module +#include "dcmtk/dcmseg/segdef.h" //for definitions +#include "dcmtk/dcmseg/segment.h" // for DcmSegment class #include "dcmtk/dcmseg/segtypes.h" // for segmentation data types -#include "dcmtk/ofstd/ofvector.h" // for OFVector +#include "dcmtk/dcmseg/segutils.h" // fo packBinaryFrame() +#include "dcmtk/ofstd/ofvector.h" // for OFVector class +#include "dcmtk/ofstd/ofdiag.h" // for DCMTK_DIAGNOSTIC_PUSH etc. -// Forward declarations -class DcmSegment; class FGSegmentation; class FGDerivationImage; class DcmFileFormat; -/** Class representing an object of the "Segmentation SOP Class". +/** Class representing an object of the "Segmentation IOD" + * or "Label Map Segmentation IOD". */ -class DCMTK_DCMSEG_EXPORT DcmSegmentation : public DcmIODImage > + +class DCMTK_DCMSEG_EXPORT DcmSegmentation : public DcmIODImage, IODImagePixelModule > { public: + + struct LoadingFlags; + // -------------------- destruction ------------------------------- /** Destructor, frees memory @@ -65,9 +74,21 @@ public: * @param filename The file to read from * @param segmentation The resulting segmentation object. NULL if dataset * could not be read successfully. + * @param flags Flags to configure the loading of the segmentation object + * @return EC_Normal if reading was successful, error otherwise + */ + static OFCondition loadFile(const OFString& filename, DcmSegmentation*& segmentation, const DcmSegmentation::LoadingFlags& flags = LoadingFlags()); + + /** Static method to load a Segmentation object from a file. + * The memory of the resulting Segmentation object has to be freed by the + * caller. + * @param filename The file to read from + * @param segmentation The resulting segmentation object. NULL if dataset + * could not be read successfully. + * @param flags Flags to configure the loading of the segmentation object * @return EC_Normal if reading was successful, error otherwise */ - static OFCondition loadFile(const OFString& filename, DcmSegmentation*& segmentation); + static OFCondition loadFile(const OFFile& filename, DcmSegmentation*& segmentation, const DcmSegmentation::LoadingFlags& flags = LoadingFlags()); /** Static method to load a Segmentation object from a dataset object. * The memory of the resulting Segmentation object has to be freed by the @@ -75,9 +96,10 @@ public: * @param dataset The dataset to read from * @param segmentation The resulting segmentation object. NULL if dataset * could not be read successfully. + * @param flags Flags to configure the loading of the segmentation object * @return EC_Normal if reading was successful, error otherwise */ - static OFCondition loadDataset(DcmDataset& dataset, DcmSegmentation*& segmentation); + static OFCondition loadDataset(DcmDataset& dataset, DcmSegmentation*& segmentation, const DcmSegmentation::LoadingFlags& flags = LoadingFlags()); /** Static method to load a concatenation of a DICOM Segmentation instance * into a DcmSegmentation object. @@ -130,6 +152,16 @@ public: */ virtual OFBool getCheckFGOnWrite(); + /** Set whether attribute values should be checked on writing, i.e. if writing + * should fail if attribute values violate their VR, VM, character set or value length. + * A missing but required value is always considered an error, independent of this setting. + * If set to OFFalse, writing will always succeed, even if attribute value constraints + * are violated. A warning instead of an error will be printed to the logger. + * @param doCheck If OFTrue, attribute value errors are handled as errors on writing, if OFFalse + * any errors are ignored. + */ + virtual void setValueCheckOnWrite(const OFBool doCheck); + /** If enabled, dimensions are checked before actual writing. * This can be very time-consuming if many frames are present. * Disabling should only be done if the user knows that the functional groups @@ -147,6 +179,14 @@ public: */ virtual OFBool getCheckDimensionsOnWrite(); + /** Get input transfer syntax. Returns EXS_Unknown if object has been + * created from scratch (and not from file or dataset). If the + * segmentation has been loaded from a concatenation, the value + * will be EXS_Unknown. + * @return Input transfer syntax + */ + virtual E_TransferSyntax getInputTransferSyntax() const; + // -------------------- creation --------------------- /** Factory method to create a binary segmentation object from the minimal @@ -170,6 +210,35 @@ public: const IODGeneralEquipmentModule::EquipmentInfo& equipmentInfo, const ContentIdentificationMacro& contentIdentification); + /** Factory method to create a labelmap segmentation object from the minimal + * set of information required. The actual segments and the frame data is + * added separately. + * The memory of the resulting Segmentation object has to be freed by the + * caller. + * @param segmentation The resulting segmentation object if provided data is + * valid. Otherwise NULL is returned. + * @param rows Number of rows of segmentation frame data + * @param columns Number of rows of segmentation frame data + * @param equipmentInfo Equipment that is responsible for creating the + * segmentation. All attributes in equipmentInfo must have + * non-empty values. + * @param contentIdentification Basic content identification information + * @param use16Bit Denote whether to use 16 bit pixel data, i.e + * allow for more than 255 segments (labels) in the segmentation + * object (up to 65535). If OFTrue, 16 bit pixel data is used, + * otherwise 8 bit. + * @param colorModel The color model to be used for the labelmap. Default + * is MONOCHROME2, alternative is PALETTE. + * @return EC_Normal if creation was successful, error otherwise + */ + static OFCondition createLabelmapSegmentation(DcmSegmentation*& segmentation, + const Uint16 rows, + const Uint16 columns, + const IODGeneralEquipmentModule::EquipmentInfo& equipmentInfo, + const ContentIdentificationMacro& contentIdentification, + const OFBool use16Bit, + const DcmSegTypes::E_SegmentationLabelmapColorModel colorModel = DcmSegTypes::SLCM_MONOCHROME2); + /** Factory method to create a fractional segmentation object from the minimal * set of information required. The actual segments and the frame data is * added separately. @@ -210,6 +279,12 @@ public: // -------------------- access --------------------- + OFBool has16BitPixelData() const; + + Uint16 getRows(); + + Uint16 getColumns(); + /** Get number of frames, based on the number of items in the shared * functional functional groups sequence (i.e.\ the attribute Number of * Frames) is not trusted). Note that this returns the numbers of frames @@ -288,7 +363,12 @@ public: * @param segmentNumber The logical segment number * @return The segment if segment number is valid, NULL otherwise */ - virtual DcmSegment* getSegment(const size_t segmentNumber); + virtual DcmSegment* getSegment(const Uint16 segmentNumber); + + /** Get all segments + * @return The resulting segments + */ + virtual const OFMap& getSegments(); /** Get logical segment number by providing a pointer to a given segment * @param segment The segment to find the logical segment number for @@ -308,7 +388,7 @@ public: * @param frameNo The number of the frame to get (starting with 0) * @return The frame requested or NULL if not existing */ - virtual const DcmIODTypes::Frame* getFrame(const size_t& frameNo); + virtual const DcmIODTypes::FrameBase* getFrame(const size_t& frameNo); /** Get the frame numbers that belong to a specific segment number * @param segmentNumber The segment to search frames for @@ -320,8 +400,16 @@ public: /** Add segment to segmentation object * @param seg The segment that should be added - * @param segmentNumber The logical segment number that was assigned for - * this segment. Contains 0 if adding failed. + * @param segmentNumber Depending on the type of segmentation, this + * parameter is handled differently: + * - For binary and fractional segmentations the segment number + * is automatically assigned and will be returned in this + * parameter. It is assigned from 0 onwards, i.e. the first + * segment added will have segment number 1, the second 2, etc. + * - For labelmap segmentations, the segment number is taken from + * this parameter and can be >= 0. If the segment number is + * already used, the method will overwrite an old segment with + * this number. * @return EC_Normal if adding was successful, error otherwise */ virtual OFCondition addSegment(DcmSegment* seg, Uint16& segmentNumber); @@ -334,20 +422,24 @@ public: /** Add frame to segmentation object * @param pixData Pixel data to be added. Length must be rows*columns bytes. - * For binary segmentations (bit depth i.e.\ Bits - * Allocated/Stored=1), each byte equal to 0 will be interpreted as - * "not set", while every other value is interpreted as "set". For - * fractional segmentations the full byte is copied as is. + * - For binary segmentations (bit depth i.e.\ Bits + * Allocated/Stored=1), each byte equal to 0 will be interpreted as + * "not set", while every other value is interpreted as "set". + * - For fractional segmentations the full byte is copied as is. + * - For labelmap segmentations, the value of each byte is interpreted + * as the segment number. In that case the segmentNumber parameters + * is ignored. * @param segmentNumber The logical segment number (>=1) this frame refers to. * The segment identified by the segmentNumber must already exist. + * For labelmap segmentations, this parameter is ignored. * @param perFrameInformation The functional groups that identify this frame (i.e. * which are planned to be not common for all other frames). The * functional groups are copied, so ownership of each group stays * with the caller no matter what the method returns. * @return EC_Normal if adding was successful, error otherwise */ - virtual OFCondition - addFrame(Uint8* pixData, const Uint16 segmentNumber, const OFVector& perFrameInformation); + template + OFCondition addFrame(T* pixData, const Uint16 segmentNumber, const OFVector& perFrameInformation); /** Return reference to content content identification of this segmentation object * @return Reference to content identification data @@ -359,6 +451,16 @@ public: */ virtual IODMultiframeDimensionModule& getDimensions(); + /** Return reference to ICC Profile Module + * @return Reference to ICC Profile Module + */ + virtual IODICCProfileModule& getICCProfile(); + + /** Return reference to Palette Color Lookup Table module + * @return Reference to Palette Color Lookup Table module + */ + virtual IODPaletteColorLUTModule& getPaletteColorLUT(); + /** Set lossy compression flag of the segmentation object to "01". If one of the * source images of this segmentation has undergone lossy compression then * this function should be called. @@ -429,11 +531,37 @@ public: */ virtual OFCondition importFromSourceImage(const OFString& filename, const OFBool takeOverCharset = OFTrue); + /// Flags for loading segmentation objects. These flags can be used to + /// configure the loading of segmentation objects. + struct LoadingFlags + { + // Number of threads to use for reading per-frame functional groups + // (will also be applied to writing, if applicable later on) + Uint32 m_numThreads; + + // Transfer syntax to use for reading/writing. + // transfer syntax used to read the data (auto detection if EXS_Unknown) + E_TransferSyntax m_readTransferSyntax; + + /** Constructor to initialize the flags */ + LoadingFlags() : m_numThreads(1), m_readTransferSyntax(EXS_Unknown) {} + + /** Clear all flags to their default values */ + void clear() + { + m_numThreads = 1; + m_readTransferSyntax = EXS_Unknown; + } + }; + + protected: + /** Protected default constructor. Library users should the factory create..() * method in order to create an object from scratch */ - DcmSegmentation(); + template + DcmSegmentation(OFin_place_type_t(ImagePixel)); /** Overwrites DcmIODImage::read() * @param dataset The dataset to read from @@ -447,7 +575,8 @@ protected: */ OFCondition readWithoutPixelData(DcmItem& dataset); - /** Writes the complete dataset without pixel data + /** Writes the complete dataset without pixel data, and write pixel data separately. + * Version for 8 bit pixel data. * @param dataset The dataset to write to * @param pixData Buffer for pixel data to write to * @param pixDataLength Length of pixData buffer @@ -455,6 +584,15 @@ protected: */ OFCondition writeWithSeparatePixelData(DcmItem& dataset, Uint8*& pixData, size_t& pixDataLength); + /** Writes the complete dataset without pixel data, and write pixel data separately + * Version for 16 bit pixel data. + * @param dataset The dataset to write to + * @param pixData Buffer for pixel data to write to + * @param pixDataLength Length of pixData buffer + * @return EC_Normal if writing succeeded, error otherwise + */ + OFCondition writeWithSeparatePixelData(DcmItem& dataset, Uint16*& pixData, size_t& pixDataLength); + /** Create those data structures common for binary and fractional * segmentations * @param segmentation The segmentation object created @@ -462,19 +600,31 @@ protected: * @param columns The number of columns for the segmentation * @param equipmentInfo Equipment information * @param contentIdentification Content meta information + * @param bitsAllocated The number of bits allocated for the pixel data + * 8 for binary and fractional segmentations, 8 or 16 for labelmaps * @return EC_Normal if creation was successful, error otherwise */ static OFCondition createCommon(DcmSegmentation*& segmentation, const Uint16 rows, const Uint16 columns, const IODGeneralEquipmentModule::EquipmentInfo& equipmentInfo, - const ContentIdentificationMacro& contentIdentification); + const ContentIdentificationMacro& contentIdentification, + const Uint16 bitsAllocated); - /** Hide same function from IODImage since do not want the user to access - * the image pixel module manually. - * @return The Image Pixel Module + /** Creates segmentation object with pixel data matching required bit depths (as defined by Bits Allocated) + * @param item The item to read Bits Allocated from (1, 8 or 16 bits permitted) + * @param segmentation The segmentation object to create + * @return EC_Normal if creation was successful, error otherwise */ - virtual IODImagePixelModule& getImagePixel(); + static OFCondition createRequiredBitDepth(DcmItem& item, DcmSegmentation*& segmentation); + + /** Creates segmentation object with pixel data matching given bit depths + * @param bitsAllocated The Bits Allocated value to use (1, 8 or 16 permitted) + * @param segmentation The segmentation object to create + * @return EC_Normal if creation was successful, error otherwise + */ + static OFCondition createRequiredBitDepth(const Uint16 bitsAllocated, DcmSegmentation*& segmentation); + /** Initialize IOD rules */ @@ -490,7 +640,8 @@ protected: * @param pixData The filled pixel data buffer returned by the method * @return EC_Normal if writing was successful, error otherwise */ - OFCondition writeFractionalFrames(Uint8* pixData); + template + OFCondition writeByteBasedFrames(T* pixData); /** Write binary frames to given given pixel data buffer * @param pixData The filled pixel data buffer returned by the method @@ -531,6 +682,15 @@ protected: */ virtual OFCondition readFrames(DcmItem& dataset); + /** Read pixel data from given pixel data element + * @param pixelData The pixel data element to read from + * @param numFrames The number of frames expected in the pixel data element + * @param pixelsPerFrame The number of pixels per frame (rows*columns) + * @param bitsAlloc Bits Allocated value (1, 8 or 16) + * @return EC_Normal if reading was successful, error otherwise + */ + virtual OFCondition readPixelData(DcmElement* pixelData, const size_t numFrames, const size_t pixelsPerFrame, const Uint16 bitsAlloc); + /** Get Image Pixel module attributes and perform some basic checking * @param dataset Item to read from, usually main dataset level * @param allocated Bits Allocated @@ -560,9 +720,41 @@ protected: * Pixel data is copied so it must be freed by the caller. * @return EC_Normal if adding was successful, error otherwise */ - virtual OFCondition addFrame(Uint8* pixData); + template + OFCondition addFrame(T* pixData); + + /** Determine color model. The color model is always MONOCHROME2, except for + * labelmaps where PALETTE is permitted. This method checks whether the + * we have a labelmap and if returns the correct string for setting Photometric + * Interpretation based on desired color model setting (m_LabelmapColorModel). + * If unknown color model is requested, MONOCHROME2 and a warning is printed. + * @return The color model string for Photometric Interpretation attribute. + */ + OFString determineColorModel(); + + /** Checks whether color model found in photometricInterpretation parameter is valid, + * i.e. MONOCHROME2, or in case of labelmaps MONOCHROME2 or PALETTE. + * Sets internal flag m_labelmapColorModel (for labelmaps) accordingly. + * @param photometricInterpretation The color model to check + * (e.g. MONOCHROME2, PALETTE, etc.) + * @return OFTrue if color model is valid, OFFalse otherwise + */ + OFBool checkColorModel(const OFString& photometricInterpretation); + + /** Sets the SOP Class UID based on the segmentation type, + * i.e. whether it is a binary or fractional (Segmentation Storage SOP Class) + * or a labelmap segmentation (Labelmap Segmentation Storage SOP Class). + * The SOP Class UID is set to the following values: + * If the segmentation type is unknown, the SOP Class UID is set to + * Segmentation Storage SOP Class as well but a warning is printed. + */ + void setSOPClassUIDBasedOnSegmentationType(); private: + + struct SetRowsAndCols; + struct SetImagePixelModuleVisitor; + // Modules supported: // // Patient Module (through DcmIODImage) @@ -575,6 +767,7 @@ private: // Enhanced General Equipment Module (through DcmIODImage) // General Image Module (through DcmIODImage) // Image Pixel Module (through DcmIODImage) + // Palette Color LUT Module (through this class) // Segmentation Image Module (through this class) // Multi-frame Functional Group Module // Multi-Frame Dimension Module @@ -593,8 +786,21 @@ private: /// Multi-frame Dimension Module IODMultiframeDimensionModule m_DimensionModule; + /// Palette Color LUT Module + IODPaletteColorLUTModule m_PaletteColorLUTModule; + + /// ICC Profile + IODICCProfileModule m_ICCProfileModule; + /// Binary frame data - OFVector m_Frames; + OFVector m_Frames; + + /// Denotes whether 16 bit pixel data is used + OFBool m_16BitPixelData; + + /// Denotes in case of label maps the color model to be used + /// (only relevant for label maps, ignored for binary and fractional segmentations) + DcmSegTypes::E_SegmentationLabelmapColorModel m_LabelmapColorModel; /* Image level information */ @@ -615,12 +821,21 @@ private: /// Maximum Fractional Value: (US, 1, 1C) (required if fractional type is FRACTIONAL) DcmUnsignedShort m_MaximumFractionalValue; - /// Segment descriptions from Segment Sequence - OFVector m_Segments; + /// Segment descriptions from Segment Sequence. + /// Maps Segment Number to Segment Description data. + /// For Labelmaps, the Segment Number is the label value, i.e. the pixel + /// value used in the pixel data to denote the segment. + OFMap m_Segments; /// Multi-frame Functional Groups high level interface FGInterface m_FGInterface; + /// Input transfer syntax; can be EXS_Unknown if object has been + /// created from scratch (and not from file or dataset). If the + /// segmentation has been loaded from a concatenation, the value + /// will be EXS_Unknown. + E_TransferSyntax m_inputXfer; + // --------------- private helper functions ------------------- /** Clear old data @@ -631,19 +846,22 @@ private: * @param pixelData The Pixel Data element * @param rows Number of rows * @param cols Number of columns + * @param bytesPerPixel Bytes per pixel (1 for 1 or 8 bit data, or 2 for 16 bit data) * @param numberOfFrames Number of frames * @result OFTrue if length is valid, OFFalse otherwise */ OFBool - checkPixDataLength(DcmElement* pixelData, const Uint16 rows, const Uint16 cols, const Uint32& numberOfFrames); + checkPixDataLength(DcmElement* pixelData, const Uint16 rows, const Uint16 cols, const Uint16 bytesPerPixel, const Uint32& numberOfFrames); /** Loads file * @param dcmff The file format to load into * @param filename The filename of the file to load * @param dset Pointer to dataset after loading + * @param xfer Transfer syntax to use for reading + * (if EXS_Unknown,the default, auto detection is used) * @return EC_Normal if loading was successful, error otherwise */ - static OFCondition loadFile(DcmFileFormat& dcmff, const OFString& filename, DcmDataset*& dset); + static OFCondition loadFile(DcmFileFormat& dcmff, const OFString& filename, DcmDataset*& dset, const E_TransferSyntax xfer); /** Computes the number of total bytes required for the frame data of this * segmentation object. Takes into account dimensions and number of frames, @@ -651,6 +869,7 @@ private: * size_t type is not able to hold the result of intermediate computations. * @param rows Number of rows of a frame * @param cols Number of cols of a frame + * @param bytesPerPixel Bytes per pixel (use 1 for 1 or 8 bit data, or 2 for 16 bit data) * @param numberOfFrames The number of frames of the object * @param bytesRequired Will hold the result of the computation, * if successful. Does not any padding into account. @@ -658,7 +877,7 @@ private: * otherwise. */ OFCondition - getTotalBytesRequired(const Uint16& rows, const Uint16& cols, const Uint32& numberOfFrames, size_t& bytesRequired); + getTotalBytesRequired(const Uint16& rows, const Uint16& cols, const Uint16& bytesPerPixel, const Uint32& numberOfFrames, size_t& bytesRequired); /** Read Fractional Type of segmentation. * @param item The item to read from @@ -682,4 +901,220 @@ private: static OFCondition decompress(DcmDataset& dset); }; + +template +OFCondition DcmSegmentation::addFrame(T* pixData) +{ + if (m_Frames.size() >= DCM_SEG_MAX_FRAMES) + return SG_EC_MaxFramesReached; + + OFCondition result; + Uint16 rows = 0; + Uint16 cols = 0; + if (getImagePixel().getRows(rows).good() && getImagePixel().getColumns(cols).good()) + { + DcmIODTypes::Frame* frame = NULL; + + // Diagnostic push/pop for Visual Studio that disables + // warning on constant expressions, in this case the + // if statement if (sizeof(T) != 1) which is known for + // each template instantiation at compile time. One can + // use constexpr if available to avoid this warning but this + // is not available in all cases + #include DCMTK_DIAGNOSTIC_PUSH + #include DCMTK_DIAGNOSTIC_IGNORE_VISUAL_STUDIO_CONSTANT_EXPRESSION_WARNING + switch (m_SegmentationType) + { + case DcmSegTypes::ST_BINARY: + { + if (sizeof(T) != 1) // 8 bit pixel data + { + DCMSEG_ERROR("Cannot add frame: 16 bit pixel data expected but 8 bit pixel data provided"); + result = IOD_EC_InvalidPixelData; + break; + } + // Pack the binary frame + frame = DcmSegUtils::packBinaryFrame(pixData, rows, cols); + if (!frame) + result = IOD_EC_CannotInsertFrame; + break; + } + case DcmSegTypes::ST_FRACTIONAL: + case DcmSegTypes::ST_LABELMAP: + { + frame = new DcmIODTypes::Frame(rows * cols); + + if (frame) + { + if (frame->m_pixData) + { + memcpy(frame->m_pixData, pixData, frame->getLengthInBytes()); + } + else + { + delete frame; + result = EC_MemoryExhausted; + } + } + else + result = EC_MemoryExhausted; + break; + } + case DcmSegTypes::ST_UNKNOWN: + default: + result = SG_EC_UnknownSegmentationType; + break; + } + // re-enable diagnostic warnings + #include DCMTK_DIAGNOSTIC_POP + if (result.good()) + { + m_Frames.push_back(frame); + } + } + else + { + DCMSEG_ERROR("Cannot add frame since rows and/or columns are unknown"); + result = IOD_EC_CannotInsertFrame; + } + return result; +} + + +/** Add frame to segmentation object + * @param pixData Pixel data to be added. Length must be rows*columns bytes. + * - For binary segmentations (bit depth i.e.\ Bits + * Allocated/Stored=1), each byte equal to 0 will be interpreted as + * "not set", while every other value is interpreted as "set". + * - For fractional segmentations the full byte is copied as is. + * - For labelmap segmentations, the value of each byte is interpreted + * as the segment number. In that case the segmentNumber parameters + * is ignored. + * @param segmentNumber The logical segment number (>=1) this frame refers to. + * The segment identified by the segmentNumber must already exist. + * For labelmap segmentations, this parameter is ignored. + * @param perFrameInformation The functional groups that identify this frame (i.e. + * which are planned to be not common for all other frames). The + * functional groups are copied, so ownership of each group stays + * with the caller no matter what the method returns. + * @return EC_Normal if adding was successful, error otherwise + */ +template +OFCondition +DcmSegmentation::addFrame(T* pixData, const Uint16 segmentNumber, const OFVector& perFrameInformation) +{ + if (m_Frames.size() >= DCM_SEG_MAX_FRAMES) + return SG_EC_MaxFramesReached; + + if (m_16BitPixelData && (sizeof(T) != 2)) + { + DCMSEG_ERROR("Cannot add frame: 16 bit pixel data expected but 8 bit pixel data provided"); + return IOD_EC_InvalidPixelData; + } + else if (!m_16BitPixelData && (sizeof(T) == 2)) + { + DCMSEG_ERROR("Cannot add frame: 8 bit pixel data expected but 16 bit pixel data provided"); + return IOD_EC_InvalidPixelData; + } + + Uint32 frameNo = OFstatic_cast(Uint32, m_Frames.size()); // will be the index of the frame (counted from 0) + OFCondition result; + + // Check input parameters + if (pixData == NULL) + { + DCMSEG_ERROR("No pixel data provided or zero length"); + result = EC_IllegalParameter; + } + if (segmentNumber == 0) + { + if (m_SegmentationType != DcmSegTypes::ST_LABELMAP) + { + DCMSEG_ERROR("Cannot add frame: Segment number 0 is not permitted for segmentation type " + << DcmSegTypes::segtype2OFString(m_SegmentationType)); + result = SG_EC_NoSuchSegment; + } + // we ignore the segment number for label maps + } + // If this is not a labelmap, check if segment the frame refers to actually exists + else if ((m_SegmentationType != DcmSegTypes::ST_LABELMAP) && (getSegment(segmentNumber) == NULL) ) + { + DCMSEG_ERROR("Cannot add frame: Segment with given number " << segmentNumber << " does not exist"); + result = SG_EC_NoSuchSegment; + } + if (result.bad()) + return result; + + OFVector::const_iterator it = perFrameInformation.begin(); + while (it != perFrameInformation.end()) + { + // Labelmap is not permitted to have Segmentation Functional Group, + // and for other segmentation types we create it automatically, ignore if found + if ((*it)->getType() == DcmFGTypes::EFG_SEGMENTATION) + { + if (m_SegmentationType == DcmSegTypes::ST_LABELMAP) + { + DCMSEG_WARN("Ignoring provided Segmentation Functional Group, not permitted for labelmap segmentation"); + it++; + continue; + } + else + { + DCMSEG_WARN("Ignoring provided Segmentation Functional Group, will be created automatically"); + it++; + continue; + } + } + result = (*it)->check(); + if (result.bad()) + { + DCMSEG_ERROR("Could not add new frame since functional group of type: " + << (*it)->getType() << " is invalid: " << result.text()); + break; + } + result = m_FGInterface.addPerFrame(frameNo, *(*it)); + if (result.bad()) + { + DCMSEG_ERROR("Could not add new frame since functional group of type " << (*it)->getType() << ": " + << result.text()); + break; + } + it++; + } + + // Now also add Segmentation Functional Group + if (result.good() && (m_SegmentationType != DcmSegTypes::ST_LABELMAP)) + { + FGSegmentation seg; + result = seg.setReferencedSegmentNumber(segmentNumber); + if (result.good()) + { + result = m_FGInterface.addPerFrame(frameNo, seg); + } + else + { + DCMSEG_ERROR("Could not add new frame, invalid segment number " << segmentNumber << ": " << result.text()); + } + } + + // Insert pixel data + if (result.good()) + { + result = addFrame(pixData); + } + + // Cleanup any per-frame groups that might have been inserted and return + if (result.bad()) + { + for (OFVector::const_iterator it2 = perFrameInformation.begin(); it2 != perFrameInformation.end(); + it2++) + { + m_FGInterface.deletePerFrame(frameNo, (*it2)->getType()); + } + } + + return result; +} + + #endif // SEGDOC_H diff --git a/dcmseg/include/dcmtk/dcmseg/segment.h b/dcmseg/include/dcmtk/dcmseg/segment.h index baf3b816..f69f1c6d 100644 --- a/dcmseg/include/dcmtk/dcmseg/segment.h +++ b/dcmseg/include/dcmtk/dcmseg/segment.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -26,17 +26,17 @@ #include "dcmtk/dcmdata/dcvrus.h" #include "dcmtk/dcmiod/iodmacro.h" -#include "dcmtk/dcmseg/segdoc.h" #include "dcmtk/dcmseg/segtypes.h" #include "dcmtk/dcmdata/dcvrui.h" #include "dcmtk/dcmdata/dcvrlo.h" #include "dcmtk/dcmdata/dcvrut.h" -/** Class representing a segment from the Segment Identification Sequence, - * as used within the Segmentation Image Module. It includes the Segment - * Description Macro. - */ +class DcmSegmentation; +/** Class that represents a Segment inside a Segmentation object. + * It mostly represents data as found in an item of the Segment Identification + * Sequence (Segmentation Image Module). + */ class DCMTK_DCMSEG_EXPORT DcmSegment { @@ -76,6 +76,25 @@ public: const DcmSegTypes::E_SegmentAlgoType algoType, const OFString& algoName = ""); + /** Make a clone of this segment. + * Note that the reference to DcmSegmentation is copied, if not provided in the parameter. + * @param seg Pointer to the DcmSegmentation object to associate with the cloned segment, + * copied from the original segment if not provided + * @return Pointer to the cloned segment if successful, OFnullptr otherwise + */ + DcmSegment* clone(DcmSegmentation* seg = NULL); + + /** Assignment operator, performs deep copy + * @param rhs The right-hand side segment to assign from + * @return Reference to this segment + */ + DcmSegment& operator=(const DcmSegment& rhs); + + /** Copy constructor + * @param rhs The right-hand side segment to copy from + */ + DcmSegment(const DcmSegment& rhs); + // ---------------- writing -------------------- /** Write segment to given item which is usually contained within @@ -259,6 +278,16 @@ public: */ virtual OFCondition setTrackingUID(const OFString& value, const OFBool checkValue = OFTrue); + /** THIS IS ONLY FOR INTERNAL USE. DO NOT USE this as a regular API user. + * Get Segment Number as read from the Segment Sequence for this segment. + * It may be different from the Segment Number as returned by getSegmentNumber(), + * i.e. do not rely on this method for anything. + * @return EC_Normal if successful, an error code otherwise + */ + virtual Uint16 getSegmentNumberRead(); + + OFshared_ptr getIODRules(); + /// The utility class must access the protected default constructor friend class DcmIODUtil; @@ -279,18 +308,16 @@ protected: void referenceSegmentationDoc(DcmSegmentation* doc); private: - /** Private undefined copy constructor - */ - DcmSegment(const DcmSegment&); - - /** Private undefined assignment operator - * @return Reference to "this" class - */ - DcmSegment& operator=(const DcmSegment&); /// The segmentation document where this segment is located in DcmSegmentation* m_SegmentationDoc; + /// The segment number as read from the Segment Number attribute. + /// This attribute only holds the number read from the file/item, + /// and will be updated only during a write process. It is not meant + /// to be used by the API user at all but only for internal purposes. + DcmUnsignedShort m_SegmentNumber; + /// Segment Description Macro SegmentDescriptionMacro m_SegmentDescription; @@ -316,7 +343,7 @@ private: DcmUniqueIdentifier m_TrackingUID; /// Rules for data elements within this IOD - IODRules m_Rules; + OFshared_ptr m_Rules; }; #endif // SEGMENT_H diff --git a/dcmseg/include/dcmtk/dcmseg/segtypes.h b/dcmseg/include/dcmtk/dcmseg/segtypes.h index a0a0dec2..9e06788f 100644 --- a/dcmseg/include/dcmtk/dcmseg/segtypes.h +++ b/dcmseg/include/dcmtk/dcmseg/segtypes.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -67,7 +67,7 @@ const Uint32 DCM_SEG_MAX_FRAMES = 2147483647; // 2^31-1 * These error codes can be used in addition to the general purpose * codes defined in module dcmdata. */ -//@{ +///@{ /// error: specified functional group is already existing extern DCMTK_DCMSEG_EXPORT const OFConditionConst SG_EC_MaxSegmentsReached; @@ -81,6 +81,26 @@ extern DCMTK_DCMSEG_EXPORT const OFConditionConst SG_EC_InvalidValue; extern DCMTK_DCMSEG_EXPORT const OFConditionConst SG_EC_NotEnoughData; /// error: too many frames extern DCMTK_DCMSEG_EXPORT const OFConditionConst SG_EC_MaxFramesReached; +/// error: invalid bit depth +extern DCMTK_DCMSEG_EXPORT const OFConditionConst SG_EC_InvalidBitDepth; +/// error: frames are not parallel +extern DCMTK_DCMSEG_EXPORT const OFConditionConst SG_EC_FramesNotParallel; +/// error: no segmentation SOP class (Segmentation or Label Map Segmentation SOP Class) +extern DCMTK_DCMSEG_EXPORT const OFConditionConst SG_EC_NoSegmentationBasedSOPClass; +/// error: segmentation-based object does not require conversion +extern DCMTK_DCMSEG_EXPORT const OFConditionConst SG_EC_NoConversionRequired; +/// error: cannot convert fractional to labelmap segmentations +extern DCMTK_DCMSEG_EXPORT const OFConditionConst SG_EC_CannotConvertFractionalToLabelmap; +/// error: segmentation-based object is already a label map +extern DCMTK_DCMSEG_EXPORT const OFConditionConst SG_EC_AlreadyLabelMap; +/// error: binary segmentation contains overlapping segments +extern DCMTK_DCMSEG_EXPORT const OFConditionConst SG_EC_OverlappingSegments; +/// error: cannot convert to PALETTE color model since not all segments contain Recommended Display CIELab Value Macro +extern DCMTK_DCMSEG_EXPORT const OFConditionConst SG_EC_CannotConvertMissingCIELab; +/// error: missing Plane Position (Patient) Functional Group +extern DCMTK_DCMSEG_EXPORT const OFConditionConst SG_EC_MissingPlanePositionPatient; + +///@} /** General purpose class hiding global functions, constants and types in the * segmentation context from the global namespace. @@ -100,7 +120,10 @@ public: ST_BINARY, /// Fractional segmentation where fraction specifies how much of voxel /// is occupied by the segment - ST_FRACTIONAL + ST_FRACTIONAL, + /// Labelmap segmentation where each segment is assigned a unique + // integer value in the pixel data (EXPERIMENTAL: based on Supplement 243 public comment 2014-01-08) + ST_LABELMAP }; /** Segment Algorithm Type @@ -129,6 +152,18 @@ public: SFT_OCCUPANCY }; + /** Labelmap Segmentation desired color model + */ + enum E_SegmentationLabelmapColorModel + { + /// Unknown (e.g.\ not initialized) + SLCM_UNKNOWN, + /// Monochrome 1 Bit + SLCM_MONOCHROME2, + /// Palette Color + SLCM_PALETTE + }; + // -- helper functions -- /** Return string representation of algorithm type @@ -163,6 +198,20 @@ public: * @return The fractional type as enum value */ static DcmSegTypes::E_SegmentationFractionalType OFString2FractionalType(const OFString& value); + + /** Returns string representation from labelmap color enum type + * @param value The labelmap color model as enum value + * @param fallbackValue The value to use if the enum value is unknown or invalid (not used if empty) + * @return The labelmap color model as a string + */ + static OFString labelmapColorModel2OFString(const DcmSegTypes::E_SegmentationLabelmapColorModel value, const OFString& fallbackValue=""); + + /** Return enum representation of photometric interpretation type string as found in + * segmentation objects. + * @param value The photometric interpretation type as a string + * @return The photometric interpretation type as enum value + */ + static DcmSegTypes::E_SegmentationLabelmapColorModel OFString2LabelmapColorModel(const OFString& value); }; /** Class representing the Segmented Property Type Code and Segmented @@ -183,6 +232,24 @@ public: */ virtual ~SegmentedPropertyTypeCodeItem(); + /** Clone method, creates a new instance of this class and performs + * a deep copy of all data. + * @return Pointer to newly created SegmentedPropertyTypeCodeItem object. The + * caller is responsible for deleting the object after use. + */ + virtual SegmentedPropertyTypeCodeItem* clone(); + + /** Assignment operator, performs deep copy + * @param rhs The right-hand side SegmentedPropertyTypeCodeItem to assign from + * @return Reference to this SegmentedPropertyTypeCodeItem + */ + SegmentedPropertyTypeCodeItem& operator=(const SegmentedPropertyTypeCodeItem& rhs); + + /** Copy constructor + * @param rhs The right-hand side SegmentedPropertyTypeCodeItem to copy from + */ + SegmentedPropertyTypeCodeItem(const SegmentedPropertyTypeCodeItem& rhs); + /** Clear all data */ virtual void clearData(); @@ -238,6 +305,19 @@ public: */ virtual ~SegmentDescriptionMacro(); + /** Clone method, creates a new instance of this class and performs + * a deep copy of all data. + * @return Pointer to newly created SegmentDescriptionMacro object. The + * caller is responsible for deleting the object after use. + */ + virtual SegmentDescriptionMacro* clone(); + + /** Assignment operator, performs deep copy + * @param rhs The right-hand side SegmentDescriptionMacro to assign from + * @return Reference to this SegmentDescriptionMacro + */ + SegmentDescriptionMacro& operator=(const SegmentDescriptionMacro& rhs); + /** Clear all data */ virtual void clearData(); diff --git a/dcmseg/include/dcmtk/dcmseg/segutils.h b/dcmseg/include/dcmtk/dcmseg/segutils.h index a8b1a91d..dc0471c6 100644 --- a/dcmseg/include/dcmtk/dcmseg/segutils.h +++ b/dcmseg/include/dcmtk/dcmseg/segutils.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -26,6 +26,7 @@ #include "dcmtk/dcmseg/segdef.h" #include "dcmtk/dcmseg/segtypes.h" +#include "dcmtk/dcmiod/iodtypes.h" #include "dcmtk/ofstd/oftypes.h" #include "dcmtk/ofstd/ofvector.h" @@ -42,7 +43,8 @@ public: * @param columns The number of columns in the pixel data * @return The frame data if successful, NULL if an error occurs */ - static DcmIODTypes::Frame* packBinaryFrame(const Uint8* pixelData, const Uint16 rows, const Uint16 columns); + template + static DcmIODTypes::Frame* packBinaryFrame(const T* pixelData, const Uint16 rows, const Uint16 columns); /** Concatenate given frames into a single bit array * @param frames The frames to concatenate. Each frame is expected to be in packed format (1 bit per pixel), @@ -54,7 +56,7 @@ public: * @param pixDataLength The length of the pixData buffer in bytes * @return EC_Normal if successful, an error code otherwise */ - static OFCondition concatBinaryFrames(const OFVector& frames, const Uint16 rows, const Uint16 cols, Uint8* pixData, const size_t pixDataLength); + static OFCondition concatBinaryFrames(const OFVector& frames, const Uint16 rows, const Uint16 cols, Uint8* pixData, const size_t pixDataLength); /** Unpacks a binary segmentation frame into a "sparse" pixel data frame where * every resulting byte represents a single bit of the frame being either @@ -64,7 +66,8 @@ public: * @param cols The cols of the frame * @return The segmentation frame in unpacked format. NULL in case of error. */ - static DcmIODTypes::Frame* unpackBinaryFrame(const DcmIODTypes::Frame* frame, Uint16 rows, Uint16 cols); + static DcmIODTypes::Frame* + unpackBinaryFrame(const DcmIODTypes::Frame* frame, Uint16 rows, Uint16 cols); /** Dumps a byte as binary number to a string. Only useful for @@ -83,6 +86,82 @@ public: * otherwise bytes are enumerated */ static void debugDumpBin(Uint8* buffer, size_t length, const OFString& what, const OFBool raw); + }; +/** Pack the given segmentation pixel data, provided "unpacked", into + * the packed format expected by DICOM. This is the default version which prints an error + * and returns NULL. + * @return Returns NULL + */ +template +DcmIODTypes::Frame* +DcmSegUtils::packBinaryFrame(const T*, const Uint16, const Uint16) +{ + // Return error since this function is not specialized for T + DCMSEG_ERROR("packBinaryFrame() can only be used for Uint8 data but you provided something else"); + return NULL; +} + +/** Pack the given segmentation pixel data, provided "unpacked", into + * the packed format expected by DICOM. This is the 8 bit version which + * is the only version actually used for segmentation objects. + * @param pixelData Pixel data in unpacked format, i.e on byte per pixel, either 0 (not set) or non-0 (set) + * @param rows Number of rows in the pixel data + * @param columns The number of columns in the pixel data + * @return Frame data if successful, NULL if an error occurs + */ +template<> +inline DcmIODTypes::Frame* +DcmSegUtils::packBinaryFrame(const Uint8* pixelData, const Uint16 rows, const Uint16 columns) +{ + // Sanity checking + const size_t totalBits = OFstatic_cast(size_t, rows) * columns; + if (totalBits == 0) + { + DCMSEG_ERROR("Unable to pack binary segmentation frame: Rows or Columns is 0"); + return NULL; + } + if (!pixelData) + { + DCMSEG_ERROR("Unable to pack binary segmentation frame: No pixel data provided"); + return NULL; + } + + // Calculate total number of bytes required + size_t totalBytes = (totalBits + 7) / 8; // +7 to round up to the nearest byte + + // Allocate memory for the packed bit array + Uint8* packedData = new Uint8[totalBytes]; + if (packedData == NULL) + { + DCMSEG_ERROR("Cannot allocate memory for packed binary frame"); + return NULL; + } + memset(packedData, 0, totalBytes); // Initialize to 0 + + // Pack the bits + for (Uint32 i = 0; i < totalBits; ++i) { + if (pixelData[i] != 0) { + Uint32 byteIndex = i / 8; + Uint32 bitIndex = i % 8; + DCMSEG_TRACE("bitIndex: " << bitIndex << ", byteIndex: " << byteIndex << ", packedData[byteIndex]: " << DcmSegUtils::debugByte2Bin(packedData[byteIndex])); + packedData[byteIndex] |= (1 << bitIndex); // Fill from right to left + } + } + + // Create and return the frame + DcmIODTypes::Frame* frame = new DcmIODTypes::Frame(); + if (frame == NULL) + { + DCMSEG_ERROR("Cannot allocate memory for packed binary frame"); + delete[] packedData; + return NULL; + } + frame->m_pixData = packedData; + frame->m_numPixels = totalBytes; // for binary frames, numPixels must be set to the number of bytes used + return frame; // Return the packed frame +} + + #endif // SEGUTILS_H diff --git a/dcmseg/libsrc/CMakeLists.txt b/dcmseg/libsrc/CMakeLists.txt index f11bfae3..faccbed5 100644 --- a/dcmseg/libsrc/CMakeLists.txt +++ b/dcmseg/libsrc/CMakeLists.txt @@ -1,5 +1,6 @@ # create library from source files DCMTK_ADD_LIBRARY(dcmseg + overlaputil.cc segdoc.cc segment.cc segtypes.cc diff --git a/dcmseg/libsrc/Makefile.dep b/dcmseg/libsrc/Makefile.dep index 1bbbdf6e..83d4caf1 100644 --- a/dcmseg/libsrc/Makefile.dep +++ b/dcmseg/libsrc/Makefile.dep @@ -1,32 +1,143 @@ -segdoc.o: segdoc.cc ../../config/include/dcmtk/config/osconfig.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcuid.h \ - ../../ofstd/include/dcmtk/ofstd/oftypes.h \ +overlaputil.o: overlaputil.cc ../include/dcmtk/dcmseg/overlaputil.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../config/include/dcmtk/config/osconfig.h \ + ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ ../../ofstd/include/dcmtk/ofstd/ofcast.h \ - ../../ofstd/include/dcmtk/ofstd/ofexport.h \ ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ - ../../dcmfg/include/dcmtk/dcmfg/concatenationcreator.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ - ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ ../../ofstd/include/dcmtk/ofstd/ofstring.h \ ../../ofstd/include/dcmtk/ofstd/ofstream.h \ - ../../ofstd/include/dcmtk/ofstd/ofstd.h \ - ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../dcmfg/include/dcmtk/dcmfg/framesorter.h \ + ../../ofstd/include/dcmtk/ofstd/ofmath.h \ + ../../dcmfg/include/dcmtk/dcmfg/fginterface.h \ + ../../dcmfg/include/dcmtk/dcmfg/fg.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgbase.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ ../../ofstd/include/dcmtk/ofstd/oferror.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ + ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgtypes.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgplanpo.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgplanor.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgpixmsr.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgseg.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ + ../include/dcmtk/dcmseg/segdoc.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ + ../../dcmfg/include/dcmtk/dcmfg/concatenationcreator.h \ + ../../dcmfg/include/dcmtk/dcmfg/concatenationloader.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodimage.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodcommn.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ + ../../dcmiod/include/dcmtk/dcmiod/modcommoninstanceref.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrlo.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ + ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodreferences.h \ + ../../dcmiod/include/dcmtk/dcmiod/modequipment.h \ + ../../dcmiod/include/dcmtk/dcmiod/modfor.h \ + ../../dcmiod/include/dcmtk/dcmiod/modgeneralseries.h \ + ../../dcmiod/include/dcmtk/dcmiod/modgeneralstudy.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpatient.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpatientstudy.h \ + ../../dcmiod/include/dcmtk/dcmiod/modsopcommon.h \ + ../../dcmiod/include/dcmtk/dcmiod/modgeneralimage.h \ + ../../dcmiod/include/dcmtk/dcmiod/modimagepixelvariant.h \ + ../../dcmiod/include/dcmtk/dcmiod/modimagepixelbase.h \ + ../../ofstd/include/dcmtk/ofstd/ofvriant.h \ + ../../ofstd/include/dcmtk/ofstd/diag/stralias.def \ + ../../dcmiod/include/dcmtk/dcmiod/modenhequipment.h \ + ../../dcmiod/include/dcmtk/dcmiod/modimagepixel.h \ + ../../dcmiod/include/dcmtk/dcmiod/modmultiframedimension.h \ + ../../dcmiod/include/dcmtk/dcmiod/modiccprofile.h \ + ../../dcmiod/include/dcmtk/dcmiod/modmultiframefg.h \ + ../../dcmiod/include/dcmtk/dcmiod/modsegmentationseries.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpalettecolorlut.h \ + ../include/dcmtk/dcmseg/segdef.h ../include/dcmtk/dcmseg/segment.h \ + ../include/dcmtk/dcmseg/segtypes.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrst.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrui.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrut.h \ + ../include/dcmtk/dcmseg/segutils.h \ + ../../ofstd/include/dcmtk/ofstd/oftimer.h +segdoc.o: segdoc.cc ../../config/include/dcmtk/config/osconfig.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ ../../oflog/include/dcmtk/oflog/oflog.h \ ../../oflog/include/dcmtk/oflog/logger.h \ @@ -41,6 +152,7 @@ segdoc.o: segdoc.cc ../../config/include/dcmtk/config/osconfig.h \ ../../oflog/include/dcmtk/oflog/appender.h \ ../../ofstd/include/dcmtk/ofstd/ofmem.h \ ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ ../../oflog/include/dcmtk/oflog/layout.h \ ../../oflog/include/dcmtk/oflog/streams.h \ @@ -52,38 +164,52 @@ segdoc.o: segdoc.cc ../../config/include/dcmtk/config/osconfig.h \ ../../oflog/include/dcmtk/oflog/logmacro.h \ ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcuid.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmfg/include/dcmtk/dcmfg/concatenationcreator.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ - ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ - ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ ../../dcmfg/include/dcmtk/dcmfg/fgdefine.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../dcmfg/include/dcmtk/dcmfg/fgderimg.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ - ../../dcmfg/include/dcmtk/dcmfg/fgbase.h \ ../../dcmfg/include/dcmtk/dcmfg/fgtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ - ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ - ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrst.h \ ../../dcmfg/include/dcmtk/dcmfg/fgseg.h \ ../../dcmiod/include/dcmtk/dcmiod/iodutil.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h \ ../include/dcmtk/dcmseg/segdoc.h \ @@ -110,33 +236,39 @@ segdoc.o: segdoc.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/modenhequipment.h \ ../../dcmiod/include/dcmtk/dcmiod/modimagepixel.h \ ../../dcmiod/include/dcmtk/dcmiod/modmultiframedimension.h \ + ../../dcmiod/include/dcmtk/dcmiod/modiccprofile.h \ ../../dcmiod/include/dcmtk/dcmiod/modmultiframefg.h \ ../../dcmiod/include/dcmtk/dcmiod/modsegmentationseries.h \ - ../include/dcmtk/dcmseg/segdef.h ../include/dcmtk/dcmseg/segtypes.h \ - ../include/dcmtk/dcmseg/segment.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpalettecolorlut.h \ + ../include/dcmtk/dcmseg/segdef.h ../include/dcmtk/dcmseg/segment.h \ + ../include/dcmtk/dcmseg/segtypes.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrui.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrut.h \ ../include/dcmtk/dcmseg/segutils.h segment.o: segment.cc ../../config/include/dcmtk/config/osconfig.h \ - ../../dcmiod/include/dcmtk/dcmiod/iodutil.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ - ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ ../../ofstd/include/dcmtk/ofstd/oftypes.h \ ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ ../../ofstd/include/dcmtk/ofstd/ofcast.h \ ../../ofstd/include/dcmtk/ofstd/ofexport.h \ - ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ - ../../ofstd/include/dcmtk/ofstd/ofstring.h \ - ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../dcmiod/include/dcmtk/dcmiod/iodutil.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ ../../ofstd/include/dcmtk/ofstd/ofstd.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../../ofstd/include/dcmtk/ofstd/oftraits.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ - ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ - ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ - ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ ../../ofstd/include/dcmtk/ofstd/oferror.h \ ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ @@ -164,7 +296,6 @@ segment.o: segment.cc ../../config/include/dcmtk/config/osconfig.h \ ../../oflog/include/dcmtk/oflog/logmacro.h \ ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ ../../oflog/include/dcmtk/oflog/tracelog.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ @@ -173,8 +304,6 @@ segment.o: segment.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ - ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ - ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ @@ -183,6 +312,7 @@ segment.o: segment.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h \ @@ -195,16 +325,16 @@ segment.o: segment.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmfg/include/dcmtk/dcmfg/fg.h \ ../../dcmfg/include/dcmtk/dcmfg/fgbase.h \ ../../dcmfg/include/dcmtk/dcmfg/fgtypes.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgseg.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ ../../dcmiod/include/dcmtk/dcmiod/iodimage.h \ ../../dcmiod/include/dcmtk/dcmiod/iodcommn.h \ ../../dcmiod/include/dcmtk/dcmiod/modcommoninstanceref.h \ ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ @@ -225,13 +355,16 @@ segment.o: segment.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/modenhequipment.h \ ../../dcmiod/include/dcmtk/dcmiod/modimagepixel.h \ ../../dcmiod/include/dcmtk/dcmiod/modmultiframedimension.h \ + ../../dcmiod/include/dcmtk/dcmiod/modiccprofile.h \ ../../dcmiod/include/dcmtk/dcmiod/modmultiframefg.h \ ../../dcmiod/include/dcmtk/dcmiod/modsegmentationseries.h \ - ../include/dcmtk/dcmseg/segdef.h ../include/dcmtk/dcmseg/segtypes.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpalettecolorlut.h \ + ../include/dcmtk/dcmseg/segdef.h ../include/dcmtk/dcmseg/segment.h \ + ../include/dcmtk/dcmseg/segtypes.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrst.h \ - ../include/dcmtk/dcmseg/segment.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrui.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrut.h + ../../dcmdata/include/dcmtk/dcmdata/dcvrut.h \ + ../include/dcmtk/dcmseg/segutils.h segtypes.o: segtypes.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ @@ -297,6 +430,7 @@ segtypes.o: segtypes.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ @@ -354,11 +488,13 @@ segutils.o: segutils.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../include/dcmtk/dcmseg/segtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ @@ -368,7 +504,6 @@ segutils.o: segutils.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ diff --git a/dcmseg/libsrc/Makefile.in b/dcmseg/libsrc/Makefile.in index 9728e81a..27153238 100644 --- a/dcmseg/libsrc/Makefile.in +++ b/dcmseg/libsrc/Makefile.in @@ -22,7 +22,7 @@ LOCALINCLUDES = -I$(ofstddir)/include -I$(oflogdir)/include \ -I$(dcmdatadir)/include -I$(dcmioddir)/include -I$(dcmfgdir)/include LOCALDEFS = -objs = segdoc.o segment.o segtypes.o segutils.o +objs = overlaputil.o segdoc.o segment.o segtypes.o segutils.o library = libdcmseg.$(LIBEXT) diff --git a/dcmseg/libsrc/overlaputil.cc b/dcmseg/libsrc/overlaputil.cc new file mode 100644 index 00000000..7747c723 --- /dev/null +++ b/dcmseg/libsrc/overlaputil.cc @@ -0,0 +1,833 @@ +/* + * + * Copyright (C) 2023-2025, Open Connections GmbH + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmqi + * + * Author: Michael Onken + * + * Purpose: Interface of class OverlapUtil + * + */ + +#include "dcmtk/dcmseg/overlaputil.h" +#include "dcmtk/dcmfg/framesorter.h" +#include "dcmtk/dcmdata/dcerror.h" +#include "dcmtk/dcmfg/fginterface.h" +#include "dcmtk/dcmfg/fgpixmsr.h" +#include "dcmtk/dcmfg/fgplanor.h" +#include "dcmtk/dcmfg/fgseg.h" +#include "dcmtk/dcmfg/fgtypes.h" +#include "dcmtk/dcmiod/iodtypes.h" +#include "dcmtk/dcmseg/segdoc.h" +#include "dcmtk/dcmseg/segtypes.h" +#include "dcmtk/dcmseg/segutils.h" +#include "dcmtk/ofstd/ofcond.h" +#include "dcmtk/ofstd/ofstream.h" +#include "dcmtk/ofstd/oftimer.h" +#include "dcmtk/ofstd/oftypes.h" + +OverlapUtil::OverlapUtil() + : m_imageOrientation() + , m_framePositions() + , m_framesForSegment() + , m_logicalFramePositions() + , m_segmentsByPosition() + , m_segmentOverlapMatrix(0) + , m_nonOverlappingSegments() + , m_seg() +{ +} + +OverlapUtil::~OverlapUtil() +{ + // nothing to do +} + +void OverlapUtil::setSegmentationObject(DcmSegmentation* seg) +{ + m_seg = seg; + clear(); +} + +void OverlapUtil::clear() +{ + m_imageOrientation.clear(); + m_framePositions.clear(); + m_framesForSegment.clear(); + m_logicalFramePositions.clear(); + m_segmentsByPosition.clear(); + m_segmentOverlapMatrix.clear(); + m_nonOverlappingSegments.clear(); +} + +OFCondition OverlapUtil::getFramesByPosition(DistinctFramePositions& result) +{ + OFCondition cond; + if (!m_seg) + { + DCMSEG_ERROR("getFramesByPosition(): No segmentation object set"); + return EC_IllegalCall; + } + if (m_logicalFramePositions.empty()) + { + cond = groupFramesByPosition(); + } + if (cond.good()) + { + result = m_logicalFramePositions; + } + return cond; +} + +OFCondition OverlapUtil::getFramesForSegment(const Uint32 segmentNumber, OFVector& frames) +{ + if (!m_seg) + { + DCMSEG_ERROR("getFramesForSegment(): No segmentation object set"); + return EC_IllegalCall; + } + if ((segmentNumber == 0) || (segmentNumber > m_seg->getNumberOfSegments())) + { + DCMSEG_ERROR("getFramesForSegment(): Segment number " << segmentNumber << " is out of range"); + return EC_IllegalParameter; + } + if (m_framesForSegment.empty()) + { + FGInterface& fg = m_seg->getFunctionalGroups(); + size_t tempNum = m_seg->getNumberOfFrames(); + if (tempNum > 4294967295) + { + DCMSEG_ERROR("getFramesForSegment(): Number of frames " << tempNum << " exceeds maximum number of possible frames (2^32-1)"); + return EC_IllegalParameter; + } + Uint32 numFrames = static_cast(m_seg->getNumberOfFrames()); + m_framesForSegment.clear(); + m_framesForSegment.resize(m_seg->getNumberOfSegments()); + // Get Segmentation FG for each frame and remember the segment number for each frame + // in the vector m_segmentsForFrame + for (Uint32 f = 0; f < numFrames; f++) + { + FGBase* group = NULL; + FGSegmentation* segFG = NULL; + group = fg.get(f, DcmFGTypes::EFG_SEGMENTATION); + segFG = OFstatic_cast(FGSegmentation*, group); + if (segFG) + { + Uint16 segNum = 0; + OFCondition cond = segFG->getReferencedSegmentNumber(segNum); + if (cond.good() && segNum > 0) + { + m_framesForSegment[segNum - 1].push_back(f); // physical frame number for segment + } + else if (segNum == 0) + { + DCMSEG_WARN("getFramesForSegment(): Referenced Segment Number is 0 (not permitted) for frame #" + << f << ", ignoring"); + return EC_InvalidValue; + } + else + { + DCMSEG_ERROR( + "getFramesForSegment(): Referenced Segment Number not found (not permitted) for frame #" + << f << ", cannot add segment"); + return EC_TagNotFound; + } + } + } + } + frames = m_framesForSegment[segmentNumber - 1]; + return EC_Normal; +} + +OFCondition OverlapUtil::ensureFramesAreParallel() +{ + FGInterface& fg = m_seg->getFunctionalGroups(); + OFCondition cond; + OFBool perFrame = OFFalse; + FGPlaneOrientationPatient* pop = NULL; + // Ensure that Image Orientation Patient is shared, i.e. we have parallel frames + m_imageOrientation.clear(); + m_imageOrientation.resize(6); + FGBase* group = fg.get(0, DcmFGTypes::EFG_PLANEORIENTPATIENT, perFrame); + pop = OFstatic_cast(FGPlaneOrientationPatient*, group); + if (pop) + { + if (perFrame == OFFalse) + { + DCMSEG_DEBUG("ensureFramesAreParallel(): Image Orientation Patient is shared, frames are parallel"); + m_imageOrientation.resize(6); + cond = pop->getImageOrientationPatient(m_imageOrientation[0], + m_imageOrientation[1], + m_imageOrientation[2], + m_imageOrientation[3], + m_imageOrientation[4], + m_imageOrientation[5]); + DCMSEG_DEBUG("Image Orientation Patient set to : " << m_imageOrientation[0] << ", " << m_imageOrientation[1] + << ", " << m_imageOrientation[2] << ", " << m_imageOrientation[3] << ", " << m_imageOrientation[4] + << ", " << m_imageOrientation[5]); + } + else + { + DCMSEG_ERROR( + "ensureFramesAreParallel(): Image Orientation Patient is per-frame, frames are probably not parallel"); + return SG_EC_FramesNotParallel; + } + } + else + { + DCMSEG_ERROR( + "ensureFramesAreParallel(): Plane Orientation (Patient) FG not found, cannot check for parallel frames"); + return EC_TagNotFound; + } + return cond; +} + +OFCondition OverlapUtil::groupFramesByPosition() +{ + if (!m_framePositions.empty()) + { + // Already computed + return EC_Normal; + } + + OFCondition cond = ensureFramesAreParallel(); + if (cond.bad()) + { + return cond; + } + + OFTimer tm; + + // Group all frames by position into m_logicalFramePositions. + // After that, all frames at the same position will be in the same vector + // assigned to the same key (the frame's coordinates) in the map. + // Group all frames by position into m_logicalFramePositions. + FrameSorterIPP sorter; + sorter.setSorterInput(&(m_seg->getFunctionalGroups())); + FrameSorterIPP::Results results; + sorter.sort(results); + if (results.errorCode.bad()) + { + DCMSEG_ERROR("groupFramesByPosition(): Cannot sort frames by position: " << results.errorCode.text()); + return results.errorCode; + } + // Copy results from frame sorter to overlap util framePositions member + m_framePositions.clear(); + m_framePositions.reserve(results.framePositions.size()); + for (size_t i = 0; i < results.framePositions.size(); ++i) + { + m_framePositions.push_back(FramePositionAndNumber(results.framePositions[i], results.frameNumbers[i])); + } + cond = groupFramesByLogicalPosition(); + + // print frame groups if debug log level is enabled: + if (cond.good() && DCM_dcmsegLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL)) + { + DCMSEG_DEBUG("groupFramesByPosition(): Frames grouped by position:"); + for (size_t i = 0; i < m_logicalFramePositions.size(); ++i) + { + OFStringStream ss; + for (size_t j = 0; j < m_logicalFramePositions[i].size(); ++j) + { + if (j > 0) + ss << ", "; + ss << m_logicalFramePositions[i][j]; + } + DCMSEG_DEBUG("groupFramesByPosition(): Logical frame #" << i << ": " << ss.str()); + } + } + DCMSEG_DEBUG("groupFramesByPosition(): Grouping frames by position took " << tm.getDiff() << " s"); + + if (cond.bad()) + { + m_framePositions.clear(); + m_logicalFramePositions.clear(); + } + return cond; +} + +OFCondition OverlapUtil::getSegmentsByPosition(SegmentsByPosition& result) +{ + if (!m_seg) + { + DCMSEG_ERROR("getSegmentsByPosition(): No segmentation object set"); + return EC_IllegalCall; + } + if (!m_segmentsByPosition.empty()) + { + // Already computed + result = m_segmentsByPosition; + return EC_Normal; + } + // Make sure prerequisites are met + OFTimer tm; + OFCondition cond = groupFramesByPosition(); + if (cond.bad()) + { + return cond; + } + size_t numSegments = m_seg->getNumberOfSegments(); + if (m_logicalFramePositions.empty()) + { + cond = getFramesByPosition(m_logicalFramePositions); + if (cond.bad()) + return cond; + } + m_segmentsByPosition.clear(); + m_segmentsByPosition.resize(m_logicalFramePositions.size()); + for (size_t l = 0; l < m_logicalFramePositions.size(); ++l) + { + OFVector segments; + for (size_t f = 0; f < m_logicalFramePositions[l].size(); ++f) + { + Uint32 frameNumber = m_logicalFramePositions[l][f]; + OFVector segs; + FGBase* group = NULL; + FGSegmentation* segFG = NULL; + group = m_seg->getFunctionalGroups().get(frameNumber, DcmFGTypes::EFG_SEGMENTATION); + segFG = OFstatic_cast(FGSegmentation*, group); + if (segFG) + { + Uint16 segNum = 0; + cond = segFG->getReferencedSegmentNumber(segNum); + if (cond.good() && segNum > 0 && (segNum <= numSegments)) + { + // check if segment is already in the list for this position + // (may happen if multiple frames for same segment are at same position?) + OFBool found = OFFalse; + for (size_t s = 0; s < m_segmentsByPosition[l].size(); ++s) + { + if (m_segmentsByPosition[l][s].m_segmentNumber == segNum) + { + found = OFTrue; + break; + } + } + if (!found) + { + m_segmentsByPosition[l].push_back(SegNumAndFrameNum(segNum, frameNumber)); + } + } + else if (segNum == 0) + { + DCMSEG_ERROR( + "getSegmentsByPosition(): Referenced Segment Number is 0 (not permitted), cannot add segment"); + cond = EC_InvalidValue; + break; + } + else if (segNum > numSegments) + { + DCMSEG_ERROR("getSegmentsByPosition(): Found Referenced Segment Number " + << segNum << " but only " << numSegments + << " segments are present, cannot add segment"); + DCMSEG_ERROR( + "getSegmentsByPosition(): Segments are not numbered consecutively, cannot add segment"); + cond = EC_InvalidValue; + break; + } + else + { + DCMSEG_ERROR("getSegmentsByPosition(): Referenced Segment Number not found (not permitted) , " + "cannot add segment"); + cond = EC_TagNotFound; + break; + } + } + } + if (cond.bad()) + { + break; + } + } + // print segments per logical frame if debug log level is enabled + if (cond.good() && DCM_dcmsegLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL)) + { + OFStringStream ss; + printSegmentsByPosition(ss); + DCMSEG_DEBUG(ss.str()); + } + DCMSEG_DEBUG("groupFramesByPosition(): Grouping segments by position took " << tm.getDiff() << " s"); + return cond; +} + +OFCondition OverlapUtil::getOverlapMatrix(OverlapMatrix& matrix) +{ + if (!m_seg) + { + DCMSEG_ERROR("getOverlapMatrix(): No segmentation object set"); + return EC_IllegalCall; + } + if (!m_segmentOverlapMatrix.empty()) + { + // Already computed + matrix = m_segmentOverlapMatrix; + return EC_Normal; + } + // Make sure prerequisites are met + OFTimer tm; + SegmentsByPosition dontCare; + OFCondition result = getSegmentsByPosition(dontCare); + if (result.good()) + { + result = buildOverlapMatrix(); + } + if (result.good()) + { + matrix = m_segmentOverlapMatrix; + } + DCMSEG_DEBUG("getOverlappingSegments(): Building overlap matrix took " << tm.getDiff() << " s"); + return result; +} + +OFCondition OverlapUtil::getNonOverlappingSegments(SegmentGroups& segmentGroups) +{ + if (!m_seg) + { + DCMSEG_ERROR("getNonOverlappingSegments(): No segmentation object set"); + return EC_IllegalCall; + } + OFTimer tm; + OFCondition result; + if (!m_nonOverlappingSegments.empty()) + { + // Already computed + segmentGroups = m_nonOverlappingSegments; + return EC_Normal; + } + // Make sure prerequisites are met + result = getOverlapMatrix(m_segmentOverlapMatrix); + if (result.good()) + { + // Group those segments from the overlap matrix together, that do not + // overlap with each other. + // Go through all segments and check if they overlap with any of the already + // grouped segments. If not, add them to the same group. If yes, create a new group + // and add them there. + m_nonOverlappingSegments.push_back(OFVector()); + for (size_t i = 0; i < m_segmentOverlapMatrix.size(); ++i) + { + // make sure we can cast this later to Uint32 + if (i > OFnumeric_limits::max()) + { + DCMSEG_ERROR("getNonOverlappingSegments(): Number of segments " + << m_segmentOverlapMatrix.size() + << " exceeds maximum number of possible segments (2^32-1)"); + return EC_IllegalParameter; + } + // Loop over all groups and check whether the current segment overlaps with any of them + OFBool overlaps = OFFalse; + for (size_t j = 0; j < m_nonOverlappingSegments.size(); ++j) + { + // Loop over all segments in the current group + for (OFVector::iterator it = m_nonOverlappingSegments[j].begin(); + it != m_nonOverlappingSegments[j].end(); + ++it) + { + // Check if the current segment overlaps with the segment in the current group + if (m_segmentOverlapMatrix[i][(*it) - 1] == 1) + { + overlaps = OFTrue; + break; + } + } + if (!overlaps) + { + // Add segment to current group + m_nonOverlappingSegments[j].push_back(OFstatic_cast(Uint32, i) + 1); + break; + } + } + if (overlaps) + { + // Create new group and add segment to it + m_nonOverlappingSegments.push_back(OFVector()); + m_nonOverlappingSegments.back().push_back(OFstatic_cast(Uint32, i) + 1); + } + } + } + DCMSEG_DEBUG("getNonOverlappingSegments(): Grouping non-overlapping segments took " << tm.getDiff() << " s"); + if (result.good()) + { + // print non-overlapping segments if debug log level is enabled + if (DCM_dcmsegLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL)) + { + OFStringStream ss; + printNonOverlappingSegments(ss); + DCMSEG_DEBUG(ss.str()); + } + } + if (result.good()) + { + segmentGroups = m_nonOverlappingSegments; + } + return result; +} + +OFBool OverlapUtil::hasOverlappingSegments() +{ + // Make sure prerequisites are met + OFCondition result = getOverlapMatrix(m_segmentOverlapMatrix); + if (result.good()) + { + for (size_t i = 0; i < m_segmentOverlapMatrix.size(); ++i) + { + for (size_t j = 0; j < m_segmentOverlapMatrix[i].size(); ++j) + { + if (m_segmentOverlapMatrix[i][j] == 1) + { + return OFTrue; + } + } + } + } + return OFFalse; +} + + +Float64 OverlapUtil::fabs(const Float64 value) +{ + return (value < 0) ? -value : value; +} + + +void OverlapUtil::printSegmentsByPosition(OFStringStream& ss) +{ + ss << "printSegmentsByPosition(): Segments grouped by logical frame positions, (seg#,frame#):" << OFendl; + for (size_t i = 0; i < m_segmentsByPosition.size(); ++i) + { + OFStringStream tempSS; + for (OFVector::iterator it = m_segmentsByPosition[i].begin(); + it != m_segmentsByPosition[i].end(); + ++it) + { + if (it != m_segmentsByPosition[i].begin()) + tempSS << ","; + tempSS << "(" << (*it).m_segmentNumber << "," << (*it).m_frameNumber << ")"; + } + ss << "printSegmentsByPosition(): Logical frame #" << i << ": " << tempSS.str(); + } +} + +void OverlapUtil::printOverlapMatrix(OFStringStream& ss) +{ + ss << "printOverlapMatrix(): Overlap matrix:" << OFendl; + for (size_t i = 0; i < m_segmentOverlapMatrix.size(); ++i) + { + for (size_t j = 0; j < m_segmentOverlapMatrix[i].size(); ++j) + { + if (m_segmentOverlapMatrix[i][j] >= 0) + ss << OFstatic_cast(Uint32, m_segmentOverlapMatrix[i][j]); + else + ss << "1"; + ss << " "; + } + ss << OFendl; + } +} + +void OverlapUtil::printNonOverlappingSegments(OFStringStream& ss) +{ + ss << "printNonOverlappingSegments(): Non-overlapping segments:" << OFendl; + for (size_t i = 0; i < m_nonOverlappingSegments.size(); ++i) + { + ss << "Group #" << i << ": "; + for (OFVector::iterator it = m_nonOverlappingSegments[i].begin(); + it != m_nonOverlappingSegments[i].end(); + ++it) + { + if (it != m_nonOverlappingSegments[i].begin()) + ss << ", "; + ss << (*it); + } + ss << OFendl; + } +} + +OFCondition OverlapUtil::buildOverlapMatrix() +{ + // Make 2 dimensional array matrix of Sint8 type for (segment numbers) X (segment numbers). + // Initialize with -1 (not checked yet) + m_segmentOverlapMatrix.clear(); + m_segmentOverlapMatrix.resize(m_seg->getNumberOfSegments()); + for (size_t i = 0; i < m_segmentOverlapMatrix.size(); ++i) + { + m_segmentOverlapMatrix[i].resize(m_seg->getNumberOfSegments(), -1); + } + // Diagonal is always 0 (segment does not interfere/overlap with itself) + for (size_t i = 0; i < m_segmentOverlapMatrix.size(); ++i) + { + m_segmentOverlapMatrix[i][i] = 0; + } + // Go through all logical frame positions, and compare all segments at each position + size_t index1, index2; + index1 = index2 = 0; + for (size_t i = 0; i < m_segmentsByPosition.size(); ++i) + { + DCMSEG_TRACE("getOverlappingSegments(): Comparing segments at logical frame position " << i); + // Compare all segments at this position + for (OFVector::iterator it = m_segmentsByPosition[i].begin(); + it != m_segmentsByPosition[i].end(); + ++it) + { + index1++; + for (OFVector::iterator it2 = m_segmentsByPosition[i].begin(); + it2 != m_segmentsByPosition[i].end(); + ++it2) + { + index2++; + // Skip comparison of same segments in reverse order (index2 < index1) + if (index2 <= index1) + continue; + // Skip self-comparison (diagonal is always 0); (index1==index2) + if (it->m_segmentNumber != it2->m_segmentNumber) + { + // Check if we already have found an overlap on another logical frame, and if so, skip + Sint8 existing_result + = m_segmentOverlapMatrix[(*it).m_segmentNumber - 1][(*it2).m_segmentNumber - 1]; + if (existing_result == 1) + { + DCMSEG_DEBUG("getOverlappingSegments(): Skipping frame comparison on pos #" + << i << " for segments " << (*it).m_segmentNumber << " and " + << (*it2).m_segmentNumber << " (already marked as overlapping)"); + continue; + } + // Compare pixels of the frames referenced by each segments. + // If they overlap, mark as overlapping + OFBool overlap = OFFalse; + checkFramesOverlap(it->m_frameNumber, it2->m_frameNumber, overlap); + + // Enter result into overlap matrix + m_segmentOverlapMatrix[(*it).m_segmentNumber - 1][(*it2).m_segmentNumber - 1] = overlap ? 1 : 0; + m_segmentOverlapMatrix[(*it2).m_segmentNumber - 1][(*it).m_segmentNumber - 1] = overlap ? 1 : 0; + } + } + } + } + // Since we don't compare all segments (since not all are showing up together on a single logical frame), + // we set all remaining entries that are still not initialized (-1) to 0 (no overlap) + for (size_t i = 0; i < m_segmentOverlapMatrix.size(); ++i) + { + for (size_t j = 0; j < m_segmentOverlapMatrix[i].size(); ++j) + { + if (m_segmentOverlapMatrix[i][j] == -1) + { + m_segmentOverlapMatrix[i][j] = 0; + } + } + } + // print overlap matrix if debug log level is enabled + if (DCM_dcmsegLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL)) + { + OFStringStream ss; + printOverlapMatrix(ss); + DCMSEG_DEBUG(ss.str()); + } + return EC_Normal; +} + +OFCondition OverlapUtil::checkFramesOverlap(const Uint32& f1, const Uint32& f2, OFBool& overlap) +{ + if (f1 == f2) + { + // The same frame should not be considered overlapping at all + overlap = OFFalse; + return EC_Normal; + } + overlap = OFFalse; + OFCondition result; + const DcmIODTypes::Frame* f1_data = OFstatic_cast(const DcmIODTypes::Frame*, m_seg->getFrame(f1)); + const DcmIODTypes::Frame* f2_data = OFstatic_cast(const DcmIODTypes::Frame*, m_seg->getFrame(f2)); + Uint16 rows, cols; + // Cast to DcmSegmentation base class (DcmIodImage) + rows = m_seg->getRows(); + cols = m_seg->getColumns(); + if (rows * cols % 8 != 0) + { + // We must compare pixel by pixel of the unpacked frames (for now) + result = checkFramesOverlapUnpacked(f1, f2, f1_data, f2_data, rows, cols, overlap); + } + else + { + // We can compare byte by byte using bitwise AND (if both have a 1 at the same position, they overlap) + result = checkFramesOverlapBinary(f1, f2, f1_data, f2_data, rows, cols, overlap); + } + if (result.good() && !overlap) + { + DCMSEG_TRACE("checkFramesOverlap(): Frames " << f1 << " and " << f2 << " don't overlap"); + } + return result; +} + +OFCondition OverlapUtil::checkFramesOverlapBinary(const Uint32& f1, + const Uint32& f2, + const DcmIODTypes::Frame* f1_data, + const DcmIODTypes::Frame* f2_data, + const Uint16& /* rows */, + const Uint16& /* cols */, + OFBool& overlap) +{ + DCMSEG_TRACE("checkFramesOverlap(): Comparing frames " << f1 << " and " << f2 << " for overlap (fast binary mode)"); + if (!f1_data || !f2_data) + { + DCMSEG_ERROR("checkFramesOverlap(): Cannot access binary frames " << f1 << " and " << f2 << " for comparison"); + return EC_IllegalCall; + } + if (f1_data->getLengthInBytes() != f2_data->getLengthInBytes()) + { + DCMSEG_ERROR("checkFramesOverlap(): Frames " << f1 << " and " << f2 + << " have different length, cannot compare"); + return EC_IllegalCall; + } + // Compare byte (8 pixels at once) using bitwise AND (if both have a 1 at the same position, they overlap) + for (size_t n = 0; n < f1_data->getLengthInBytes(); ++n) + { + if (f1_data->m_pixData[n] & f2_data->m_pixData[n]) + { + DCMSEG_DEBUG("checkFramesOverlap(): Frames " << f1 << " and " << f2 << " do overlap, pixel value " + << OFstatic_cast(Uint16, f1_data->m_pixData[n]) << " at index " + << n << " is the same"); + overlap = OFTrue; + break; + } + } + return EC_Normal; +} + +OFCondition OverlapUtil::checkFramesOverlapUnpacked(const Uint32& f1, + const Uint32& f2, + const DcmIODTypes::Frame* f1_data, + const DcmIODTypes::Frame* f2_data, + const Uint16& rows, + const Uint16 cols, + OFBool& overlap) +{ + DCMSEG_TRACE("checkFramesOverlap(): Comparing frames " << f1 << " and " << f2 + << " for overlap (slow unpacked mode)"); + OFunique_ptr > f1_unpacked(DcmSegUtils::unpackBinaryFrame(f1_data, rows, cols)); + OFunique_ptr > f2_unpacked(DcmSegUtils::unpackBinaryFrame(f2_data, rows, cols)); + if (!f1_unpacked || !f2_unpacked) + { + DCMSEG_ERROR("checkFramesOverlap(): Cannot unpack frames " << f1 << " and " << f2 << " for comparison"); + return EC_IllegalCall; + } + if (f1_unpacked->getLengthInBytes() != f2_unpacked->getLengthInBytes()) + { + DCMSEG_ERROR("checkFramesOverlap(): Frames " << f1 << " and " << f2 + << " have different length, cannot compare"); + return EC_IllegalCall; + } + // Compare pixels of both frames and check whether at least one has the same value + DCMSEG_TRACE("checkFramesOverlap(): Comparing frames " << f1 << " and " << f2 << " for overlap"); + for (size_t n = 0; n < f1_unpacked->getLengthInBytes(); ++n) + { + if (f1_unpacked->m_pixData[n] != 0 && (f1_unpacked->m_pixData[n] == f2_unpacked->m_pixData[n])) + { + DCMSEG_DEBUG("checkFramesOverlap(): Frames " << f1 << " and " << f2 << " do overlap, pixel value " + << OFstatic_cast(Uint16, f1_unpacked->m_pixData[n]) + << " at index " << n << " is the same"); + overlap = OFTrue; + break; + } + } + return EC_Normal; +} + + +OFCondition OverlapUtil::groupFramesByLogicalPosition() +{ + OFCondition cond; + FGInterface& fg = m_seg->getFunctionalGroups(); + OFBool perFrame = OFFalse; + Float64 sliceThickness = 0.0; + FGPixelMeasures* pm = NULL; + FGBase* group = fg.get(0, DcmFGTypes::EFG_PIXELMEASURES, perFrame); + pm = OFstatic_cast(FGPixelMeasures*, group); + if (pm) + { + // Get/compute Slice Thickness + cond = pm->getSliceThickness(sliceThickness); + if (cond.bad()) + { + DCMSEG_ERROR("groupFramesByPosition(): Cannot get Slice Thickness from Pixel Measures FG: " + << cond.text()); + return cond; + } + } + + Uint8 relevantCoordinate = identifyChangingCoordinate(m_imageOrientation); + + // vec will contain all frame numbers that are at the same position + OFVector vec; + vec.push_back(m_framePositions[0].m_frameNumber); + m_logicalFramePositions.push_back(vec); // Initialize for first logical frame + for (size_t j = 1; j < m_framePositions.size(); ++j) + { + // If frame is close to previous frame, add it to the same vector. + // 2.5 is chosen since it means the frames are not further away if clearly less than half a slice + // thickness + Float64 diff = OverlapUtil::fabs(m_framePositions[j].m_position[relevantCoordinate] + - m_framePositions[j - 1].m_position[relevantCoordinate]); + DCMSEG_DEBUG("Coordinates of both frames:"); + DCMSEG_DEBUG("Frame " << j << ": " << m_framePositions[j].m_position[0] << ", " + << m_framePositions[j].m_position[1] << ", " + << m_framePositions[j].m_position[2]); + DCMSEG_DEBUG("Frame " << j - 1 << ": " << m_framePositions[j - 1].m_position[0] << ", " + << m_framePositions[j - 1].m_position[1] << ", " + << m_framePositions[j - 1].m_position[2]); + DCMSEG_DEBUG("groupFramesByPosition(): Frame " << j << " is " << diff + << " mm away from previous frame"); + // 1% inaccuracy for slice thickness will be considered as same logical position + if (diff < sliceThickness * 0.01) + { + // Add frame to last vector + DCMSEG_DEBUG("Assigning to same frame bucket as previous frame"); + m_logicalFramePositions.back().push_back( + m_framePositions[j].m_frameNumber); // physical frame number + } + else + { + DCMSEG_DEBUG("Assigning to same new frame bucket"); + // Create new vector + OFVector newVec; + newVec.push_back(m_framePositions[j].m_frameNumber); + m_logicalFramePositions.push_back(newVec); + } + } + + return cond; +} + +Uint8 OverlapUtil::identifyChangingCoordinate(const OFVector& imageOrientation) +{ + Float64 cross_product[3]; + // Compute cross product of image orientation vectors. + // We are only interested into the absolute values for later comparison + cross_product[0] = OverlapUtil::fabs(imageOrientation[1] * imageOrientation[5] - imageOrientation[2] * imageOrientation[4]); + cross_product[1] = OverlapUtil::fabs(imageOrientation[2] * imageOrientation[3] - imageOrientation[0] * imageOrientation[5]); + cross_product[2] = OverlapUtil::fabs(imageOrientation[0] * imageOrientation[4] - imageOrientation[1] * imageOrientation[3]); + // Find out which coordinate is changing the most (biggest absolute coordinate value of cross product) + if ((cross_product[0] > cross_product[1]) && (cross_product[0] > cross_product[2])) + { + return 0; + } + if ((cross_product[1] > cross_product[0]) && (cross_product[1] > cross_product[2])) + { + return 1; + } + if ((cross_product[2] > cross_product[0]) && (cross_product[2] > cross_product[1])) + { + return 2; + } + // No clear winner + return 3; +} diff --git a/dcmseg/libsrc/segdoc.cc b/dcmseg/libsrc/segdoc.cc index 38798886..396b30e4 100644 --- a/dcmseg/libsrc/segdoc.cc +++ b/dcmseg/libsrc/segdoc.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * * All rights reserved. See COPYRIGHT file for details. * @@ -21,26 +21,103 @@ #include "dcmtk/config/osconfig.h" +#include "dcmtk/dcmdata/dcdeftag.h" +#include "dcmtk/dcmdata/dctypes.h" #include "dcmtk/dcmdata/dcuid.h" +#include "dcmtk/dcmdata/dcxfer.h" #include "dcmtk/dcmfg/concatenationcreator.h" #include "dcmtk/dcmfg/fgderimg.h" #include "dcmtk/dcmfg/fgseg.h" +#include "dcmtk/dcmiod/iodtypes.h" #include "dcmtk/dcmiod/iodutil.h" #include "dcmtk/dcmseg/segdoc.h" #include "dcmtk/dcmseg/segment.h" #include "dcmtk/dcmseg/segtypes.h" #include "dcmtk/dcmseg/segutils.h" -#include "dcmtk/oflog/loglevel.h" -#include +#include "dcmtk/ofstd/ofutil.h" -// default constructor (protected, instance creation via create() function) -DcmSegmentation::DcmSegmentation() - : DcmSegmentation::IODImage(OFin_place >) + +struct DcmSegmentation::SetRowsAndCols +{ + SetRowsAndCols(const Uint16 rows, const Uint16 cols) + : m_rows(rows) + , m_cols(cols) + { + } + + template + OFCondition operator()(T& t) + { + t.setRows(m_rows); + t.setColumns(m_cols); + return EC_Normal; + } + + // Members + const Uint16 m_rows; + const Uint16 m_cols; +}; + + +struct DcmSegmentation::SetImagePixelModuleVisitor +{ + SetImagePixelModuleVisitor(const Uint16 rows, const Uint16 columns, const Uint16 bitsAllocated, const Uint16 bitsStored, + const Uint16 highBit, const Uint16 samplesPerPixel, const OFString& photometricInterpretation) + : m_rows(rows) + , m_cols(columns) + , m_bitsAllocated(bitsAllocated) + , m_bitsStored(bitsStored) + , m_highBit(highBit) + , m_samplesPerPixel(samplesPerPixel) + , m_photometricInterpretation(photometricInterpretation) + , m_pixelRepresentation(2) // invalid default value + { + } + + template + OFCondition operator()(T& t) + { + if ((m_rows == 0) || (m_cols == 0)) + { + DCMSEG_ERROR("Rows/Cols must be non-zero but are : " << m_rows << "/" << m_cols); + return IOD_EC_InvalidDimensions; + } + + t.setRows(m_rows); + t.setColumns(m_cols); + t.setBitsAllocated(m_bitsAllocated); + t.setBitsStored(m_bitsStored); + t.setHighBit(m_highBit); + t.setSamplesPerPixel(m_samplesPerPixel); + t.setPhotometricInterpretation(m_photometricInterpretation); + t.setPixelRepresentation(OFis_signed::value ? 1 : 0); + + return EC_Normal; + } + + // Members + const Uint16 m_rows; + const Uint16 m_cols; + const Uint16 m_bitsAllocated; + const Uint16 m_bitsStored; + const Uint16 m_highBit; + const Uint16 m_samplesPerPixel; + const OFString m_photometricInterpretation; + const Uint16 m_pixelRepresentation; + +}; + +template +DcmSegmentation::DcmSegmentation(OFin_place_type_t(ImagePixel)) + : IODImage(OFin_place) , m_SegmentationSeries(DcmSegmentation::IODImage::getData(), DcmSegmentation::IODImage::getRules()) , m_EnhancedGeneralEquipmentModule(DcmSegmentation::IODImage::getData(), DcmSegmentation::IODImage::getRules()) , m_FG(DcmSegmentation::IODImage::getData(), DcmSegmentation::IODImage::getRules()) , m_DimensionModule(DcmSegmentation::IODImage::getData(), DcmSegmentation::IODImage::getRules()) + , m_PaletteColorLUTModule(DcmSegmentation::IODImage::getData(), DcmSegmentation::IODImage::getRules()) , m_Frames() + , m_16BitPixelData(OFFalse) + , m_LabelmapColorModel(DcmSegTypes::SLCM_UNKNOWN) , m_ImageType("DERIVED\\PRIMARY") , m_ContentIdentificationMacro() , m_SegmentationType(DcmSegTypes::ST_BINARY) @@ -48,6 +125,7 @@ DcmSegmentation::DcmSegmentation() , m_MaximumFractionalValue(DCM_MaximumFractionalValue) , m_Segments() , m_FGInterface() + , m_inputXfer(EXS_Unknown) { DcmSegmentation::initIODRules(); } @@ -57,35 +135,39 @@ void DcmSegmentation::initIODRules() // ------------ Segmentation Image Module ------------- // Partly overrides rules from General Image Module - getRules()->addRule(new IODRule(DCM_ImageType, "2", "1", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), OFTrue); - getRules()->addRule(new IODRule(DCM_SegmentationType, "1", "1", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), - OFTrue); - getRules()->addRule( + DcmSegmentation::IODImage::getRules()->addRule( + new IODRule(DCM_ImageType, "2", "1", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), OFTrue); + DcmSegmentation::IODImage::getRules()->addRule( + new IODRule(DCM_SegmentationType, "1", "1", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), OFTrue); + DcmSegmentation::IODImage::getRules()->addRule( new IODRule(DCM_SegmentationFractionalType, "1", "1C", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), OFTrue); - getRules()->addRule( + DcmSegmentation::IODImage::getRules()->addRule( new IODRule(DCM_MaximumFractionalValue, "1", "1C", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), OFTrue); + // Instance Number is Type 1 in Segmentation Image Module + DcmSegmentation::IODImage::getRules()->addRule( + new IODRule(DCM_InstanceNumber, "1", "1", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), OFTrue); + // Re-use General Image Module instead of Segmentation Image Module - getRules()->addRule(new IODRule(DCM_LossyImageCompression, "1", "1", "GeneralImageModule", DcmIODTypes::IE_IMAGE), - OFTrue); - getRules()->addRule( + DcmSegmentation::IODImage::getRules()->addRule( + new IODRule(DCM_LossyImageCompression, "1", "1", "GeneralImageModule", DcmIODTypes::IE_IMAGE), OFTrue); + DcmSegmentation::IODImage::getRules()->addRule( new IODRule(DCM_LossyImageCompressionMethod, "1-n", "1C", "GeneralImageModule", DcmIODTypes::IE_IMAGE), OFTrue); - getRules()->addRule( + DcmSegmentation::IODImage::getRules()->addRule( new IODRule(DCM_LossyImageCompressionRatio, "1-n", "1C", "GeneralImageModule", DcmIODTypes::IE_IMAGE), OFTrue); - // Override rule from General Series Module - getRules()->addRule(new IODRule(DCM_ReferencedPerformedProcedureStepSequence, - "1", - "1C", - "SegmentationSeriesModule", - DcmIODTypes::IE_SERIES), - OFTrue); - getRules()->addRule(new IODRule(DCM_SeriesNumber, "1", "1", "SegmentationSeriesModule", DcmIODTypes::IE_SERIES), - OFTrue); + // ------------ Segmentation Series Module ------------- - // Instance Number is also used within Content Identification Macro, disable it there - m_ContentIdentificationMacro.getIODRules().deleteRule(DCM_InstanceNumber); + // Override rule from General Series Module + DcmSegmentation::IODImage::getRules()->addRule(new IODRule(DCM_ReferencedPerformedProcedureStepSequence, + "1", + "1C", + "SegmentationSeriesModule", + DcmIODTypes::IE_SERIES), + OFTrue); + DcmSegmentation::IODImage::getRules()->addRule( + new IODRule(DCM_SeriesNumber, "1", "1", "SegmentationSeriesModule", DcmIODTypes::IE_SERIES), OFTrue); } DcmSegmentation::~DcmSegmentation() @@ -94,35 +176,40 @@ DcmSegmentation::~DcmSegmentation() } // static method for loading segmentation objects -OFCondition DcmSegmentation::loadFile(const OFString& filename, DcmSegmentation*& segmentation) +OFCondition DcmSegmentation::loadFile(const OFString& filename, DcmSegmentation*& segmentation, const DcmSegmentation::LoadingFlags& flags) { DcmFileFormat dcmff; DcmDataset* dataset = NULL; - OFCondition result = loadFile(dcmff, filename, dataset); + OFCondition result = loadFile(dcmff, filename, dataset, flags.m_readTransferSyntax); if (result.bad()) return result; - return loadDataset(*dataset, segmentation); + return loadDataset(*dataset, segmentation, flags); } + // static method for loading segmentation objects -OFCondition DcmSegmentation::loadDataset(DcmDataset& dataset, DcmSegmentation*& segmentation) +OFCondition DcmSegmentation::loadDataset(DcmDataset& dataset, DcmSegmentation*& segmentation, const DcmSegmentation::LoadingFlags& flags) { segmentation = NULL; OFCondition result = DcmSegmentation::decompress(dataset); if (result.bad()) return result; - DcmSegmentation* temp = new DcmSegmentation(); - if (temp == NULL) + DcmSegmentation *temp = NULL; + result = createRequiredBitDepth(dataset, temp); + if (result.bad()) { - return EC_MemoryExhausted; + return result; } - + // Apply the loading flags + temp->getFunctionalGroups().setUseThreads(flags.m_numThreads); + // Start actual reading result = temp->read(dataset); if (result.good()) { segmentation = temp; + segmentation->m_inputXfer = dataset.getOriginalXfer(); } else { @@ -137,22 +224,29 @@ OFCondition DcmSegmentation::loadConcatenation(ConcatenationLoader& cl, { DcmDataset dset; segmentation = NULL; - OFVector frames; + OFVector frames; OFCondition result = cl.load(concatenationUID, &dset, frames); if (result.good()) { - segmentation = new DcmSegmentation(); - if (segmentation) + result = createRequiredBitDepth(dset, segmentation); + if (result.good()) { - result = segmentation->readWithoutPixelData(dset); - if (result.good()) + if (segmentation) { - segmentation->m_Frames = frames; + result = segmentation->readWithoutPixelData(dset); + if (result.good()) + { + segmentation->m_Frames = frames; + // We don't check the transfer syntax in the input files, + // so even all files had the same transfer syntax, we always + // set it to EXS_Unknown. + segmentation->m_inputXfer = EXS_Unknown; + } + } + else + { + result = EC_MemoryExhausted; } - } - else - { - result = EC_MemoryExhausted; } } if (result.bad()) @@ -170,7 +264,7 @@ OFCondition DcmSegmentation::createBinarySegmentation(DcmSegmentation*& segmenta const ContentIdentificationMacro& contentIdentification) { - OFCondition result = createCommon(segmentation, rows, columns, equipmentInfo, contentIdentification); + OFCondition result = createCommon(segmentation, rows, columns, equipmentInfo, contentIdentification, 8); if (result.bad()) return result; @@ -179,6 +273,31 @@ OFCondition DcmSegmentation::createBinarySegmentation(DcmSegmentation*& segmenta return result; } + +OFCondition DcmSegmentation::createLabelmapSegmentation(DcmSegmentation *&segmentation, + const Uint16 rows, + const Uint16 columns, + const IODGeneralEquipmentModule::EquipmentInfo &equipmentInfo, + const ContentIdentificationMacro &contentIdentification, + const OFBool use16Bit, + const DcmSegTypes::E_SegmentationLabelmapColorModel colorModel) +{ + if (colorModel == DcmSegTypes::SLCM_UNKNOWN) + { + DCMSEG_ERROR("Cannot create labelmap segmentation: Color model not set"); + return EC_IllegalParameter; + } + + OFCondition result = createCommon(segmentation, rows, columns, equipmentInfo, contentIdentification, use16Bit ? 16 : 8); + if (result.bad()) + return result; + + segmentation->m_SegmentationType = DcmSegTypes::ST_LABELMAP; + segmentation->m_16BitPixelData = use16Bit; + segmentation->m_LabelmapColorModel = colorModel; + return result; +} + OFCondition DcmSegmentation::createFractionalSegmentation(DcmSegmentation*& segmentation, const Uint16 rows, const Uint16 columns, @@ -187,7 +306,7 @@ OFCondition DcmSegmentation::createFractionalSegmentation(DcmSegmentation*& segm const IODGeneralEquipmentModule::EquipmentInfo& equipmentInfo, const ContentIdentificationMacro& contentIdentification) { - OFCondition result = createCommon(segmentation, rows, columns, equipmentInfo, contentIdentification); + OFCondition result = createCommon(segmentation, rows, columns, equipmentInfo, contentIdentification, 8); if (result.bad()) return result; @@ -202,7 +321,8 @@ OFCondition DcmSegmentation::createCommon(DcmSegmentation*& segmentation, const Uint16 rows, const Uint16 columns, const IODGeneralEquipmentModule::EquipmentInfo& equipmentInfo, - const ContentIdentificationMacro& contentIdentification) + const ContentIdentificationMacro& contentIdentification, + const Uint16 bitsAllocated) { if ((rows == 0) || (columns == 0)) { @@ -210,27 +330,19 @@ OFCondition DcmSegmentation::createCommon(DcmSegmentation*& segmentation, return EC_IllegalParameter; } - segmentation = new DcmSegmentation(); + OFCondition result = createRequiredBitDepth(bitsAllocated, segmentation); if (segmentation == NULL) return EC_MemoryExhausted; segmentation->getImagePixel().setRows(rows); segmentation->getImagePixel().setColumns(columns); - OFCondition result = segmentation->setContentIdentification(contentIdentification); + DCMSEG_DEBUG("Setting segmentation content identification"); + result = segmentation->setContentIdentification(contentIdentification); if (result.good()) { - OFString tempstr; - contentIdentification.getInstanceNumber(tempstr); - result = segmentation->getGeneralImage().setInstanceNumber(tempstr); - if (result.bad()) - { - delete segmentation; - segmentation = NULL; - return EC_InvalidValue; - } - DcmIODUtil::setContentDateAndTimeNow(segmentation->getGeneralImage()); + DCMSEG_DEBUG("Setting segmentation equipment information"); result = segmentation->setEquipmentInfo(equipmentInfo, OFTrue /* check */); } @@ -243,6 +355,44 @@ OFCondition DcmSegmentation::createCommon(DcmSegmentation*& segmentation, return result; } +OFCondition DcmSegmentation::createRequiredBitDepth(const Uint16 bitsAllocated, DcmSegmentation*& seg) +{ + OFCondition result; + seg = NULL; + if (bitsAllocated == 16) + { + seg = new DcmSegmentation(OFin_place >); + } + else if ( (bitsAllocated == 8) || (bitsAllocated == 1) ) + { + seg = new DcmSegmentation(OFin_place >); + } + else + { + DCMSEG_ERROR("Cannot read segmentation object: Bits Allocated is neither 8 nor 16"); + result = IOD_EC_InvalidPixelData; + } + if (result.good() && (seg == NULL)) + { + result = EC_MemoryExhausted; + } + return result; + +} + +OFCondition DcmSegmentation::createRequiredBitDepth(DcmItem& item, DcmSegmentation*& seg) +{ + Uint16 bitsAllocated = 0; + OFCondition result = item.findAndGetUint16(DCM_BitsAllocated, bitsAllocated); + if (result.bad()) + { + DCMSEG_ERROR("Cannot read Bits Allocated from dataset: " << result.text()); + return result; + } + return createRequiredBitDepth(bitsAllocated, seg); +} + + FGDerivationImage* DcmSegmentation::createDerivationImageFG(const OFVector& derivationImages, const OFString& derivationDescription) @@ -264,14 +414,17 @@ OFCondition DcmSegmentation::read(DcmItem& dataset) OFCondition DcmSegmentation::readWithoutPixelData(DcmItem& dataset) { OFString sopClass; - if (DcmIODUtil::checkSOPClass(&dataset, UID_SegmentationStorage, sopClass).bad()) + if (DcmIODUtil::checkSOPClass(&dataset, UID_SegmentationStorage, sopClass).bad() + && DcmIODUtil::checkSOPClass(&dataset, UID_LabelMapSegmentationStorage, sopClass).bad()) { DCMSEG_ERROR("Given file does not seem to be a segmentation storage object since SOP class is: " << sopClass); return IOD_EC_WrongSOPClass; } // Read attributes in base classes - DcmSegmentation::IODImage::read(dataset); + OFCondition result = DcmSegmentation::IODImage::read(dataset); + if (result.bad()) + DCMSEG_WARN("Problem with reading Image Pixel attributes: " << result.text() << " (ignoring)"); // Read Segmentation Series Module m_SegmentationSeries.read(dataset); @@ -294,13 +447,24 @@ OFCondition DcmSegmentation::readWithoutPixelData(DcmItem& dataset) readSegments(dataset); - readSegmentationFractionalType(dataset); + if (m_SegmentationType == DcmSegTypes::ST_FRACTIONAL) + readSegmentationFractionalType(dataset); m_ContentIdentificationMacro.read(dataset); + // Read Photometric Interpretation and check whether it fits the segmentation type + OFString colorModel; + if (dataset.findAndGetOFString(DCM_PhotometricInterpretation, colorModel).good() && (colorModel == "PALETTE COLOR")) + { + // For palette, read Palette Color LUT Module and ICC Profile Module + m_PaletteColorLUTModule.read(dataset); + m_ICCProfileModule.read(dataset); + } + checkColorModel(colorModel); + // Read specific segmentation elements DcmIODUtil::getAndCheckElementFromDataset( - dataset, m_MaximumFractionalValue, getRules()->getByTag(DCM_MaximumFractionalValue)); + dataset, m_MaximumFractionalValue, DcmSegmentation::IODImage::getRules()->getByTag(DCM_MaximumFractionalValue)); return EC_Normal; } @@ -315,6 +479,16 @@ OFBool DcmSegmentation::getCheckFGOnWrite() return m_FGInterface.getCheckOnWrite(); } +void DcmSegmentation::setValueCheckOnWrite(const OFBool doCheck) +{ + m_SegmentationSeries.setValueCheckOnWrite(doCheck); + m_EnhancedGeneralEquipmentModule.setValueCheckOnWrite(doCheck); + m_PaletteColorLUTModule.setValueCheckOnWrite(doCheck); + m_DimensionModule.setValueCheckOnWrite(doCheck); + m_ICCProfileModule.setValueCheckOnWrite(doCheck); + DcmSegmentation::IODImage::setValueCheckOnWrite(doCheck); +} + void DcmSegmentation::setCheckDimensionsOnWrite(const OFBool doCheck) { m_DimensionModule.setCheckOnWrite(doCheck); @@ -325,6 +499,11 @@ OFBool DcmSegmentation::getCheckDimensionsOnWrite() return m_DimensionModule.getCheckOnWrite(); } +E_TransferSyntax DcmSegmentation::getInputTransferSyntax() const +{ + return m_inputXfer; +} + OFCondition DcmSegmentation::writeWithSeparatePixelData(DcmItem& dataset, Uint8*& pixData, size_t& pixDataLength) { // FGInterface::write() will know whether it has to check FG structure @@ -337,9 +516,134 @@ OFCondition DcmSegmentation::writeWithSeparatePixelData(DcmItem& dataset, Uint8* OFCondition result; // -- Set constant default values written by external modules -- - getGeneralImage().setLossyImageCompression("00"); - getGeneralImage().setImageType(m_ImageType); - getSOPCommon().setSOPClassUID(UID_SegmentationStorage); + DcmSegmentation::IODImage::getGeneralImage().setLossyImageCompression("00"); + DcmSegmentation::IODImage::getGeneralImage().setImageType(m_ImageType); + // Set Instance Number if not set + OFString InstanceNumber; + getData()->findAndGetOFString(DCM_InstanceNumber, InstanceNumber); + if (InstanceNumber.empty()) + { + DcmSegmentation::IODImage::getGeneralImage().setInstanceNumber("1"); + } + + // Set SOP Class UID based on segmentation type + setSOPClassUIDBasedOnSegmentationType(); + + // -- Extra Study level data -- + + // Enhanced Equipment Module + if (result.good()) + result = m_EnhancedGeneralEquipmentModule.write(dataset); + + // -- Extra Series level data -- + + // Write segmentation-specific series level attribute (Segmentation Series Module) + if (result.good()) + result = m_SegmentationSeries.write(dataset); + + // -- Extra Image level data -- + + // Write Multi-Frame Functional Groups Module + if (result.good()) + result = writeMultiFrameFunctionalGroupsModule(dataset); + + // Write Multi-Frame Dimension Module + if (result.good()) + result = writeMultiFrameDimensionModule(dataset); + + // Write Palette Color Lookup Table Module and ICC Profile Module + if (result.good()) + { + if (m_LabelmapColorModel == DcmSegTypes::SLCM_PALETTE) + { + result = m_PaletteColorLUTModule.write(dataset); + if (result.good()) + { + result = m_ICCProfileModule.write(dataset); + } + } + } + + // Write segmentation image module and image pixel module + if (result.good()) + result = writeSegmentationImageModule(dataset); + + // -- Write common multi frame image IOD attributes -- + + // Patient Module + // General Study Module + // General Series Module + // Frame of Reference Module + // General Equipment Module + // General Image Module + // Multi-frame Functional Groups Module (except functional groups itself) + // SOP Common Module + // Common Instance Reference Module + if (result.good()) + result = DcmSegmentation::IODImage::write(dataset); + + // Write frame pixel data + if (result.good()) + { + Uint32 numFrames = DcmIODUtil::limitMaxFrames( + m_Frames.size(), "More than 2147483647 frames provided, will only write 2147483647"); + Uint16 rows, cols, bytesPerPixel; + rows = cols = bytesPerPixel = 0; + DcmSegmentation::getImagePixel().getRows(rows); + DcmSegmentation::getImagePixel().getColumns(cols); + DcmSegmentation::getImagePixel().getBitsAllocated(bytesPerPixel); + if (bytesPerPixel >= 8) // 8 or 16 + { + bytesPerPixel = bytesPerPixel / 8; + } + result = getTotalBytesRequired(rows, cols, bytesPerPixel, numFrames, pixDataLength); + if (result.bad()) + return result; + + pixData = new Uint8[pixDataLength]; + if (!pixData) + return EC_MemoryExhausted; + + switch (m_SegmentationType) + { + case DcmSegTypes::ST_BINARY: + result = writeBinaryFrames(pixData, rows, cols, pixDataLength); + break; + case DcmSegTypes::ST_FRACTIONAL: + case DcmSegTypes::ST_LABELMAP: + result = writeByteBasedFrames(pixData); + break; + default: + result = SG_EC_UnknownSegmentationType; + } + if (result.bad()) + { + delete[] pixData; + } + } + + return result; +} + +OFCondition DcmSegmentation::writeWithSeparatePixelData(DcmItem& dataset, Uint16*& pixData, size_t& numPixelDataBytes) +{ + // FGInterface::write() will know whether it has to check FG structure + // so we do not need to check FG structure here (OFFalse). + if (!check(OFFalse)) + { + return IOD_EC_InvalidObject; + } + + OFCondition result; + + // -- Set constant default values written by external modules -- + + DcmSegmentation::IODImage::getGeneralImage().setLossyImageCompression("00"); + // Labelmap segmentations have a different SOP Class UID + DcmSegmentation::IODImage::getGeneralImage().setImageType(m_ImageType); + + // Set SOP Class UID based on segmentation type + setSOPClassUIDBasedOnSegmentationType(); // -- Extra Study level data -- @@ -363,6 +667,19 @@ OFCondition DcmSegmentation::writeWithSeparatePixelData(DcmItem& dataset, Uint8* if (result.good()) result = writeMultiFrameDimensionModule(dataset); + // Write Palette Color Lookup Table Module and ICC Profile Module + if (result.good()) + { + if (m_LabelmapColorModel == DcmSegTypes::SLCM_PALETTE) + { + result = m_PaletteColorLUTModule.write(dataset); + if (result.good()) + { + result = m_ICCProfileModule.write(dataset); + } + } + } + // Write segmentation image module and image pixel module if (result.good()) result = writeSegmentationImageModule(dataset); @@ -386,24 +703,39 @@ OFCondition DcmSegmentation::writeWithSeparatePixelData(DcmItem& dataset, Uint8* { Uint32 numFrames = DcmIODUtil::limitMaxFrames( m_Frames.size(), "More than 2147483647 frames provided, will only write 2147483647"); - Uint16 rows, cols; - rows = cols = 0; + Uint16 rows, cols, bytesPerPixel; + rows = cols = bytesPerPixel = 0; getImagePixel().getRows(rows); getImagePixel().getColumns(cols); - result = getTotalBytesRequired(rows, cols, numFrames, pixDataLength); + getImagePixel().getBitsAllocated(bytesPerPixel); + if (bytesPerPixel >= 8) // 8 or 16 + { + bytesPerPixel = bytesPerPixel / 8; + } + result = getTotalBytesRequired(rows, cols, bytesPerPixel, numFrames, numPixelDataBytes); if (result.bad()) return result; - pixData = new Uint8[pixDataLength]; + pixData = new Uint16[numPixelDataBytes / 2]; // 16 bit data if (!pixData) return EC_MemoryExhausted; - if (m_SegmentationType == DcmSegTypes::ST_BINARY) - result = writeBinaryFrames(pixData, rows, cols, pixDataLength); - else if (m_SegmentationType == DcmSegTypes::ST_FRACTIONAL) - result = writeFractionalFrames(pixData); - else - result = SG_EC_UnknownSegmentationType; + switch (m_SegmentationType) + { + case DcmSegTypes::ST_BINARY: + DCMSEG_ERROR("Binary segmentations must be instantiated with 8 bit pixel data (Uint8)"); + result = IOD_EC_InvalidPixelData; + break; + case DcmSegTypes::ST_FRACTIONAL: + DCMSEG_ERROR("Fractional segmentations must be instantiated with 8 bit pixel data (Uint8)"); + result = IOD_EC_InvalidPixelData; + break; + case DcmSegTypes::ST_LABELMAP: + result = writeByteBasedFrames(pixData); + break; + default: + result = SG_EC_UnknownSegmentationType; + } if (result.bad()) { delete[] pixData; @@ -428,6 +760,25 @@ size_t DcmSegmentation::getNumberOfFrames() return m_FGInterface.getNumberOfFrames(); } +OFBool DcmSegmentation::has16BitPixelData() const +{ + return this->m_16BitPixelData; +} + +Uint16 DcmSegmentation::getRows() +{ + Uint16 rows = 0; + DcmSegmentation::getImagePixel().getRows(rows); + return rows; +} + +Uint16 DcmSegmentation::getColumns() +{ + Uint16 cols = 0; + DcmSegmentation::getImagePixel().getColumns(cols); + return cols; +} + size_t DcmSegmentation::getNumberOfSegments() { return m_Segments.size(); @@ -445,7 +796,6 @@ IODSegmentationSeriesModule& DcmSegmentation::getSegmentationSeriesModule() OFCondition DcmSegmentation::addSegment(DcmSegment* seg, Uint16& segmentNumber) { - segmentNumber = 0; if (seg == NULL) return EC_IllegalParameter; @@ -453,77 +803,35 @@ OFCondition DcmSegmentation::addSegment(DcmSegment* seg, Uint16& segmentNumber) { return SG_EC_MaxSegmentsReached; } - - // Casting is safe since we made sure number of segments fits into 16 bit - segmentNumber = OFstatic_cast(Uint16, m_Segments.size() + 1); - m_Segments.push_back(seg); - return EC_Normal; -} - -OFCondition DcmSegmentation::addFrame(Uint8* pixData) -{ - if (m_Frames.size() >= DCM_SEG_MAX_FRAMES) - return SG_EC_MaxFramesReached; - - OFCondition result; - Uint16 rows = 0; - Uint16 cols = 0; - if (getImagePixel().getRows(rows).good() && getImagePixel().getColumns(cols).good()) + // For labelmaps, use the given segment number + if (m_SegmentationType == DcmSegTypes::ST_LABELMAP) { - DcmIODTypes::Frame* frame = NULL; - if (m_SegmentationType == DcmSegTypes::ST_BINARY) - { - frame = DcmSegUtils::packBinaryFrame(pixData, rows, cols); - if (!frame) - { - result = IOD_EC_CannotInsertFrame; - } - // with debug logging enabled, print out the binary frame - if (DCM_dcmsegLogger.isEnabledFor(dcmtk::log4cplus::DEBUG_LOG_LEVEL)) - { - DCMSEG_DEBUG("Added binary segmentation frame:"); - DcmSegUtils::debugDumpBin(frame->pixData, frame->length, "Binary frame", OFTrue); - } - } - else // fractional - { - frame = new DcmIODTypes::Frame(); - if (frame) - { - frame->length = rows * cols; - frame->pixData = new Uint8[frame->length]; - if (frame->pixData) - { - memcpy(frame->pixData, pixData, frame->length); - } - else - { - delete frame; - result = EC_MemoryExhausted; - } - } - else - result = EC_MemoryExhausted; - } - if (result.good()) - { - m_Frames.push_back(frame); - } + m_Segments.insert(OFMake_pair(segmentNumber, seg)); + return EC_Normal; } - else + // For binary/fractional, start with 1 if no segment exists + if (m_Segments.empty()) { - DCMSEG_ERROR("Cannot add frame since rows and/or columns are unknown"); - result = IOD_EC_CannotInsertFrame; + segmentNumber = 1; + m_Segments.insert(OFMake_pair(segmentNumber, seg)); + return EC_Normal; } - return result; + // Use next free segment number and insert + // (i.e. get the highest segment number and increment by 1). + OFMap::iterator it = m_Segments.end(); + it--; + segmentNumber = it->first + 1; + m_Segments.insert(OFMake_pair(segmentNumber, seg)); + return EC_Normal; } + SOPInstanceReferenceMacro& DcmSegmentation::getReferencedPPS() { - return getSeries().getReferencedPPS(); + return DcmSegmentation::IODImage::getSeries().getReferencedPPS(); } -const DcmIODTypes::Frame* DcmSegmentation::getFrame(const size_t& frameNo) +const DcmIODTypes::FrameBase* DcmSegmentation::getFrame(const size_t& frameNo) { if (frameNo > m_Frames.size() - 1) { @@ -561,83 +869,6 @@ OFCondition DcmSegmentation::addForAllFrames(const FGBase& group) return m_FGInterface.addShared(group); } -OFCondition -DcmSegmentation::addFrame(Uint8* pixData, const Uint16 segmentNumber, const OFVector& perFrameInformation) -{ - if (m_Frames.size() >= DCM_SEG_MAX_FRAMES) - return SG_EC_MaxFramesReached; - - Uint32 frameNo = OFstatic_cast(Uint32, m_Frames.size()); // will be the index of the frame (counted from 0) - OFCondition result; - - // Check input parameters - if (pixData == NULL) - { - DCMSEG_ERROR("No pixel data provided or zero length"); - result = EC_IllegalParameter; - } - if (segmentNumber > m_Segments.size()) - { - DCMSEG_ERROR("Cannot add frame: Segment with given number " << segmentNumber << " does not exist"); - result = SG_EC_NoSuchSegment; - } - if (result.bad()) - return result; - - OFVector::const_iterator it = perFrameInformation.begin(); - while (it != perFrameInformation.end()) - { - result = (*it)->check(); - if (result.bad()) - { - DCMSEG_ERROR("Could not add new frame since functional group of type: " - << (*it)->getType() << " is invalid: " << result.text()); - break; - } - result = m_FGInterface.addPerFrame(frameNo, *(*it)); - if (result.bad()) - { - DCMSEG_ERROR("Could not add new frame since functional group of type " << (*it)->getType() << ": " - << result.text()); - break; - } - it++; - } - - // Now also add Segmentation Functional Group - if (result.good()) - { - FGSegmentation seg; - result = seg.setReferencedSegmentNumber(segmentNumber); - if (result.good()) - { - result = m_FGInterface.addPerFrame(frameNo, seg); - } - else - { - DCMSEG_ERROR("Could not add new frame, invalid segment number " << segmentNumber << ": " << result.text()); - } - } - - // Insert pixel data - if (result.good()) - { - result = addFrame(pixData); - } - - // Cleanup any per-frame groups that might have been inserted and return - if (result.bad()) - { - for (OFVector::const_iterator it2 = perFrameInformation.begin(); it2 != perFrameInformation.end(); - it2++) - { - m_FGInterface.deletePerFrame(frameNo, (*it2)->getType()); - } - } - - return result; -} - ContentIdentificationMacro& DcmSegmentation::getContentIdentification() { return m_ContentIdentificationMacro; @@ -648,14 +879,24 @@ IODMultiframeDimensionModule& DcmSegmentation::getDimensions() return m_DimensionModule; } +IODPaletteColorLUTModule& DcmSegmentation::getPaletteColorLUT() +{ + return m_PaletteColorLUTModule; +} + +IODICCProfileModule& DcmSegmentation::getICCProfile() +{ + return m_ICCProfileModule; +} + OFCondition DcmSegmentation::setLossyImageCompressionFlag(const OFString& ratios, const OFString& methods, const OFBool checkValues) { - OFCondition result = getGeneralImage().setLossyImageCompression("01"); + OFCondition result = DcmSegmentation::IODImage::getGeneralImage().setLossyImageCompression("01"); if (result.good() || !checkValues) - result = getGeneralImage().setLossyImageCompressionMethod(methods); + result = DcmSegmentation::IODImage::getGeneralImage().setLossyImageCompressionMethod(methods); if (result.good() || !checkValues) - result = getGeneralImage().setLossyImageCompressionRatio(ratios); + result = DcmSegmentation::IODImage::getGeneralImage().setLossyImageCompressionRatio(ratios); if (checkValues) return result; @@ -672,25 +913,37 @@ OFCondition DcmSegmentation::saveFile(const OFString& filename, const E_Transfer #endif ) { - DcmXfer ts(writeXfer); + if ((writeXfer == EXS_RLELossless) && (m_SegmentationType != DcmSegTypes::ST_LABELMAP)) + { + DcmXfer ts(writeXfer); #ifdef WITH_ZLIB - DCMSEG_ERROR("Cannot write transfer syntax: " << ts.getXferName() + DCMSEG_ERROR("Cannot write transfer syntax: " << ts.getXferName() << ": Can only write uncompressed or Deflated)"); #else - if (writeXfer == EXS_DeflatedLittleEndianExplicit) - { - DCMSEG_ERROR("Cannot write transfer syntax: " - << ts.getXferName() << ": Deflate (ZLIB) support disabled, can only write uncompressed"); - } + if (writeXfer == EXS_DeflatedLittleEndianExplicit) + { + DCMSEG_ERROR("Cannot write transfer syntax: " + << ts.getXferName() << ": Deflate (ZLIB) support disabled"); + } #endif - return EC_CannotChangeRepresentation; + return EC_CannotChangeRepresentation; + } } DcmFileFormat dcmff; OFCondition result = writeDataset(*(dcmff.getDataset())); if (result.good()) { - result = dcmff.saveFile(filename.c_str(), writeXfer); + if (dcmff.chooseRepresentation(writeXfer, NULL).good() && dcmff.getDataset()->canWriteXfer(writeXfer)) + { + result = dcmff.saveFile(filename.c_str(), writeXfer, EET_ExplicitLength); + } + else + { + DcmXfer ts(writeXfer); + DCMSEG_ERROR("Cannot write transfer syntax: " << ts.getXferName()); + result = EC_CannotChangeRepresentation; + } } if (result.bad()) { @@ -745,34 +998,46 @@ OFCondition DcmSegmentation::setContentIdentification(const ContentIdentificatio /* -- Getter for DICOM attributes -- */ -DcmSegment* DcmSegmentation::getSegment(const size_t segmentNumber) +DcmSegment* DcmSegmentation::getSegment(const Uint16 segmentNumber) { // check for invalid index - if ((segmentNumber == 0) || (segmentNumber > m_Segments.size())) + if ( (m_SegmentationType != DcmSegTypes::ST_LABELMAP) && (segmentNumber == 0) ) + { + DCMSEG_ERROR("Cannot get segment 0: No such Segment Number allowed segmentation if segmentation is of type " << DcmSegTypes::segtype2OFString(m_SegmentationType)); + return NULL; + } + OFMap::iterator it = m_Segments.find(segmentNumber); + if (it == m_Segments.end()) { return NULL; } - // logical segment numbering starts with 1, so subtract 1 for vector index - return m_Segments[segmentNumber - 1]; + return it->second; } OFBool DcmSegmentation::getSegmentNumber(const DcmSegment* segment, size_t& segmentNumber) { - segmentNumber = 0; - size_t max = m_Segments.size(); - for (size_t count = 0; count < max; count++) + if (segment == NULL) + return OFFalse; + + OFMap::iterator it = m_Segments.begin(); + while (it != m_Segments.end()) { - if (m_Segments.at(count) == segment) + if (it->second == segment) { - segmentNumber = OFstatic_cast(Uint16, count + 1); + segmentNumber = it->first; return OFTrue; } + it++; } - // not found return OFFalse; } +const OFMap& DcmSegmentation::getSegments() +{ + return m_Segments; +} + OFCondition DcmSegmentation::getModality(OFString& value, const long signed int pos) const { (void)pos; @@ -809,15 +1074,51 @@ OFCondition DcmSegmentation::importFromSourceImage(DcmItem& dataset, const bool OFCondition DcmSegmentation::writeSegments(DcmItem& item) { OFCondition result; + // writeSubSequence cannot handle a map, copy to vector instead and use that for this purpose + OFVector segments; + OFMap::iterator it = m_Segments.begin(); + while (it != m_Segments.end()) + { + segments.push_back(it->second); + it++; + } DcmIODUtil::writeSubSequence >( - result, DCM_SegmentSequence, m_Segments, item, "1-n", "1", "SegmentationImageModule"); + result, DCM_SegmentSequence, segments, item, "1-n", "1", "SegmentationImageModule"); return result; } OFCondition DcmSegmentation::readSegments(DcmItem& item) { - return DcmIODUtil::readSubSequence >( - item, DCM_SegmentSequence, m_Segments, "1-n", "1", "SegmentationImageModule"); + // readSubSequence cannot handle a map, read into vector instead and fill into map afterwards + OFVector segments; + + OFCondition result = DcmIODUtil::readSubSequence >( + item, DCM_SegmentSequence, segments, "1-n", "1", "SegmentationImageModule"); + if (result.good()) + { + for (size_t count = 0; count < segments.size(); count++) + { + if (result.good()) + { + if (m_Segments.insert(OFMake_pair(segments[count]->getSegmentNumberRead(), segments[count])).second + == false) + { + DCMSEG_ERROR("Cannot insert segment " << segments[count]->getSegmentNumberRead() + << " since it already exists"); + result = EC_IllegalCall; + break; + } + } + else + { + DCMSEG_ERROR("Cannot read segment number for segment " << count << ": " << result.text()); + result = EC_IllegalCall; + break; + } + } + } + + return result; } OFCondition DcmSegmentation::readFrames(DcmItem& dataset) @@ -839,61 +1140,88 @@ OFCondition DcmSegmentation::readFrames(DcmItem& dataset) DcmElement* pixelData = NULL; if (dataset.findAndGetElement(DCM_PixelData, pixelData).bad()) return IOD_EC_InvalidPixelData; - if (!checkPixDataLength(pixelData, rows, cols, numberOfFrames)) + Uint16 bytesPerPixel = (allocated >= 8) ? allocated / 8 : 1; + if (!checkPixDataLength(pixelData, rows, cols, bytesPerPixel, numberOfFrames)) return IOD_EC_InvalidPixelData; + if (bytesPerPixel == 2) + { + m_16BitPixelData = OFTrue; + } /* Get pixel data values */ - Uint8* pixels = NULL; - result = pixelData->getUint8Array(pixels); + size_t pixelsPerFrame = OFstatic_cast(size_t, rows) * cols; + result = readPixelData(pixelData, numberOfFrames, pixelsPerFrame, allocated); + return result; +} + +OFCondition DcmSegmentation::readPixelData(DcmElement* pixelData, const size_t numFrames, const size_t pixelsPerFrame, const Uint16 bitsAlloc) +{ + Uint8* pixels8 = NULL; + Uint16* pixels16 = NULL; + OFCondition result; + if (( bitsAlloc == 1) || (bitsAlloc == 8)) + { + result = pixelData->getUint8Array(pixels8); + } + else if (bitsAlloc == 16) + { + result = pixelData->getUint16Array(pixels16); + } + else + { + DCMSEG_ERROR("Cannot read pixel data: Bits Allocated is neither 1, 8 nor 16"); + return IOD_EC_InvalidPixelData; + } if (result.bad()) { DCMSEG_ERROR("Cannot read pixel data"); return result; } - /* Read all frames into dedicated data structure */ - size_t pixelsPerFrame = OFstatic_cast(size_t, rows) * cols; - if (m_SegmentationType == DcmSegTypes::ST_BINARY) - { - result = DcmIODUtil::extractBinaryFrames(pixels, numberOfFrames, pixelsPerFrame, m_Frames); - if (result.bad()) - { - return result; - } - // with debug logging enabled, print out the binary frames - if (DCM_dcmsegLogger.isEnabledFor(dcmtk::log4cplus::DEBUG_LOG_LEVEL)) - { - DCMSEG_DEBUG("Extracted binary segmentation frame:"); - for (size_t count = 0; count < m_Frames.size(); count++) - { - DCMSEG_DEBUG("Frame " << count << ":"); - DcmIODTypes::Frame* frame = m_Frames[count]; - DcmSegUtils::debugDumpBin(frame->pixData, frame->length, "Binary frame", OFTrue); - } - } - } - else if (m_SegmentationType == DcmSegTypes::ST_FRACTIONAL) + switch (m_SegmentationType) { - for (size_t count = 0; count < numberOfFrames; count++) - { - DcmIODTypes::Frame* frame = new DcmIODTypes::Frame(); - if (!frame) - return EC_MemoryExhausted; - frame->length = pixelsPerFrame; - frame->pixData = new Uint8[pixelsPerFrame]; - if (!frame->pixData) + case DcmSegTypes::ST_BINARY: + result = DcmIODUtil::extractBinaryFrames(pixels8, numFrames, pixelsPerFrame, m_Frames); + break; + case DcmSegTypes::ST_FRACTIONAL: + case DcmSegTypes::ST_LABELMAP: + for (size_t count = 0; count < numFrames; count++) { - delete frame; - return EC_MemoryExhausted; + DcmIODTypes::FrameBase* frame = NULL; + if (bitsAlloc == 8) + { + frame = new DcmIODTypes::Frame(pixelsPerFrame); + } + else if (bitsAlloc == 16) + { + frame = new DcmIODTypes::Frame(pixelsPerFrame); + } + if (!frame || !frame->getPixelData()) + { + delete frame; + result = EC_MemoryExhausted; + break; + } + if (bitsAlloc == 8) + { + memcpy(frame->getPixelData(), pixels8 + count * pixelsPerFrame, pixelsPerFrame); + } + else // 16 + { + memcpy(frame->getPixelData(), pixels16 + count * pixelsPerFrame, frame->getLengthInBytes()); + } + // print frame->pixData to cout + // frame->print(); + m_Frames.push_back(frame); } - memcpy(frame->pixData, pixels + count * pixelsPerFrame, pixelsPerFrame); - m_Frames.push_back(frame); - } + break; + case DcmSegTypes::ST_UNKNOWN: + result = SG_EC_UnknownSegmentationType; } - return result; } + OFCondition DcmSegmentation::getAndCheckImagePixelAttributes(DcmItem& dataset, Uint16& allocated, Uint16& stored, @@ -944,32 +1272,63 @@ OFCondition DcmSegmentation::getAndCheckImagePixelAttributes(DcmItem& dataset, } } - Uint16 depth = 0; - if (m_SegmentationType == DcmSegTypes::ST_BINARY) - depth = 1; - else - depth = 8; - - if (allocated != depth) + switch (m_SegmentationType) { - DCMSEG_WARN("Bits Allocated is not set correctly (" - << allocated << ", ignored), assuming value " << depth << " as required for " - << DcmSegTypes::segtype2OFString(m_SegmentationType) << " segmentation objects"); - allocated = depth; + case DcmSegTypes::ST_BINARY: + if (allocated != 1) + { + DCMSEG_WARN("Bits Allocated is not set correctly (" + << allocated << ", ignored), assuming value 1 as required for binary segmentation objects"); + allocated = 1; + } + break; + case DcmSegTypes::ST_FRACTIONAL: + if (allocated != 8) + { + DCMSEG_WARN("Bits Allocated is not set correctly (" + << allocated + << ", ignored), assuming value 8 as required for fractional segmentation " + "objects"); + allocated = 8; + } + break; + case DcmSegTypes::ST_LABELMAP: + if ((allocated != 8) && (allocated != 16)) + { + DCMSEG_ERROR("Bits Allocated is not set correctly (" << allocated << ", giving up"); + fail = OFTrue; + } + break; + case DcmSegTypes::ST_UNKNOWN: + fail = OFTrue; + break; + } + if (fail) + { + return EC_InvalidValue; } - if (stored != depth) + if (stored != allocated) { DCMSEG_WARN("Bits Stored is not set correctly (" - << stored << ", ignored), assuming value " << depth << " as required for " + << stored << ", ignored), assuming value " << allocated << " as required for " << DcmSegTypes::segtype2OFString(m_SegmentationType) << " segmentation objects"); - stored = depth; + stored = allocated; + } + if (m_SegmentationType == DcmSegTypes::ST_BINARY) + { + if (high != 0) + { + DCMSEG_WARN("High Bit is not set correctly (" + << high << ", ignored), assuming value 0 as required for binary segmentation objects"); + high = 0; + } } - if (high != depth - 1) + else if (high != allocated - 1) { DCMSEG_WARN("High Bit is not set correctly (" - << high << ", ignored), assuming value " << depth - 1 << " as required for " + << high << ", ignored), assuming value " << allocated - 1 << " as required for " << DcmSegTypes::segtype2OFString(m_SegmentationType) << " segmentation objects"); - high = depth - 1; + high = allocated - 1; } if (spp != 1) { @@ -983,11 +1342,11 @@ OFCondition DcmSegmentation::getAndCheckImagePixelAttributes(DcmItem& dataset, << pixelRep << ", ignored), assuming value 0 as required for segmentation objects"); pixelRep = 0; } - if (colorModel != "MONOCHROME2") + if ((colorModel != "MONOCHROME2") + && ((colorModel != "PALETTE COLOR") && (m_SegmentationType == DcmSegTypes::ST_LABELMAP))) { - DCMSEG_WARN("Photometric Interpretation is not set correctly (ignored), assuming value MONOCHROME2 as required " - "for segmentation objects"); - colorModel = "MONOCHROME2"; + DCMSEG_WARN("Photometric Interpretation is not set correctly (" << colorModel << "): Must be MONOCHROME2 or PALETTE COLOR (only Labelmap segmentations)"); + fail = OFTrue; } if (rows == 0) { @@ -1008,38 +1367,67 @@ OFCondition DcmSegmentation::getAndCheckImagePixelAttributes(DcmItem& dataset, OFCondition DcmSegmentation::writeDataset(DcmItem& dataset) { - Uint8* pixData = NULL; - size_t pixDataLength; - OFCondition result = writeWithSeparatePixelData(dataset, pixData, pixDataLength); + Uint8* pixData8 = NULL; + Uint16* pixData16 = NULL; + size_t pixDataLength = 0; + OFCondition result; + if (has16BitPixelData()) + { + result = writeWithSeparatePixelData(dataset, pixData16, pixDataLength); + } + else + { + result = writeWithSeparatePixelData(dataset, pixData8, pixDataLength); + } if (result.good()) { // Check whether pixel data length exceeds maximum number of bytes for uncompressed pixel data, // enforced by length field of Pixel Data attribute VR OB/OW if written in explicit VR transfer syntax. if (pixDataLength <= 4294967294UL) { - result - = dataset.putAndInsertUint8Array(DCM_PixelData, pixData, OFstatic_cast(unsigned long, pixDataLength)); + if (has16BitPixelData()) + { + result = dataset.putAndInsertUint16Array(DCM_PixelData, pixData16, OFstatic_cast(unsigned long, pixDataLength / 2)); + } + else + { + result = dataset.putAndInsertUint8Array(DCM_PixelData, pixData8, OFstatic_cast(unsigned long, pixDataLength)); + } } else { result = FG_EC_PixelDataTooLarge; } - delete[] pixData; + delete[] pixData8; + delete[] pixData16; } return result; } OFCondition DcmSegmentation::writeConcatenation(ConcatenationCreator& cc) { - Uint8* pixData = NULL; size_t pixDataLength = 0; DcmItem* item = new DcmItem(); if (!item) return EC_MemoryExhausted; - OFCondition result = writeWithSeparatePixelData(*item, pixData, pixDataLength); - if (result.good()) + OFCondition result; + if (has16BitPixelData()) + { + Uint16* pixData = NULL; + result = writeWithSeparatePixelData(*item, pixData, pixDataLength); + if (result.good()) + { + result = cc.setCfgInput(item, pixData, pixDataLength, OFTrue /* transfer ownership */); + } + } + else { - result = cc.setCfgInput(item, pixData, pixDataLength, OFTrue /* transfer ownership */); + Uint8* pixData = NULL; + result = writeWithSeparatePixelData(*item, pixData, pixDataLength); + if (result.good()) + { + result = cc.setCfgInput(item, pixData, pixDataLength, OFTrue /* transfer ownership */); + } } return result; } @@ -1060,13 +1448,15 @@ OFCondition DcmSegmentation::writeMultiFrameDimensionModule(DcmItem& dataset) return m_DimensionModule.write(dataset); } -OFCondition DcmSegmentation::writeFractionalFrames(Uint8* pixData) +template +OFCondition DcmSegmentation::writeByteBasedFrames(T* pixData) { - OFVector::iterator it = m_Frames.begin(); + Uint8 bytesPerPixel = sizeof(T); + typename OFVector::iterator it = m_Frames.begin(); // Just copy bytes for each frame as is for (size_t count = 0; it != m_Frames.end(); count++) { - memcpy(pixData + count * (*it)->length, (*it)->pixData, (*it)->length); + memcpy(pixData + count * (*it)->getLengthInBytes() / bytesPerPixel, (*it)->getPixelData(), (*it)->getLengthInBytes()); it++; } return EC_Normal; @@ -1084,13 +1474,17 @@ OFCondition DcmSegmentation::writeSegmentationImageModule(DcmItem& dataset) dataset.putAndInsertOFStringArray(DCM_ImageType, "DERIVED\\PRIMARY"); OFCondition result = m_ContentIdentificationMacro.write(dataset); + OFString colorModel; /* Write hardcoded values */ if (result.good()) - { - getImagePixel().setSamplesPerPixel(1); - getImagePixel().setPhotometricInterpretation("MONOCHROME2"); - getImagePixel().setPixelRepresentation(0); + { Uint16 rows, cols, bitsAlloc, bitsStored, highBit, pixelRep, samplesPixel; + rows = cols = bitsAlloc = bitsStored = highBit = pixelRep = samplesPixel = 0; + OFString photometricInterpretation; + samplesPixel = 1; + + colorModel = determineColorModel(); + pixelRep = 0; /* Write Bits Allocated/Stored, High Bit, Segmentation Fractional Type, * Segmentation Type, Maximum Fractional Value @@ -1099,29 +1493,45 @@ OFCondition DcmSegmentation::writeSegmentationImageModule(DcmItem& dataset) { case DcmSegTypes::ST_BINARY: { - getImagePixel().setBitsAllocated(1); - getImagePixel().setBitsStored(1); - getImagePixel().setHighBit(0); + bitsAlloc = 1; + bitsStored = 1; + highBit = 1; dataset.putAndInsertOFStringArray(DCM_SegmentationType, "BINARY"); + dataset.putAndInsertOFStringArray(DCM_SegmentsOverlap, "UNDEFINED"); break; } case DcmSegTypes::ST_FRACTIONAL: + case DcmSegTypes::ST_LABELMAP: { - getImagePixel().setBitsAllocated(8); - getImagePixel().setBitsStored(8); - getImagePixel().setHighBit(7); - dataset.putAndInsertOFStringArray(DCM_SegmentationType, "FRACTIONAL"); - if (m_SegmentationFractionalType == DcmSegTypes::SFT_OCCUPANCY) + Uint8 numBits = 8; + if (has16BitPixelData()) { - dataset.putAndInsertOFStringArray(DCM_SegmentationFractionalType, "OCCUPANCY"); + numBits = 16; + } + bitsAlloc = bitsStored = numBits; + highBit = numBits -1; + + if (m_SegmentationType == DcmSegTypes::ST_LABELMAP) + { + dataset.putAndInsertOFStringArray(DCM_SegmentationType, "LABELMAP"); + dataset.putAndInsertOFStringArray(DCM_SegmentsOverlap, "NO"); } else { - dataset.putAndInsertOFStringArray(DCM_SegmentationFractionalType, "PROBABILITY"); + dataset.putAndInsertOFStringArray(DCM_SegmentationType, "FRACTIONAL"); + dataset.putAndInsertOFStringArray(DCM_SegmentsOverlap, "UNDEFINED"); + if (m_SegmentationFractionalType == DcmSegTypes::SFT_OCCUPANCY) + { + dataset.putAndInsertOFStringArray(DCM_SegmentationFractionalType, "OCCUPANCY"); + } + else + { + dataset.putAndInsertOFStringArray(DCM_SegmentationFractionalType, "PROBABILITY"); + } + // Maximum Fractional Value: Attribute is type 1C but "required if .. FRACTIONAL", i.e. write type 1 + DcmIODUtil::copyElementToDataset( + result, dataset, m_MaximumFractionalValue, "1", "1", "SegmentationImageModule"); } - // Maximum Fractional Value: Attribute is type 1C but "required if .. FRACTIONAL", i.e. write type 1 - DcmIODUtil::copyElementToDataset( - result, dataset, m_MaximumFractionalValue, "1", "1", "SegmentationImageModule"); break; } case DcmSegTypes::ST_UNKNOWN: @@ -1131,13 +1541,26 @@ OFCondition DcmSegmentation::writeSegmentationImageModule(DcmItem& dataset) break; } } + getImagePixel().getRows(rows); + getImagePixel().getColumns(cols); + if (bitsAlloc == 16) + { + result = SetImagePixelModuleVisitor(rows, cols,bitsAlloc, bitsStored, highBit, samplesPixel, colorModel) + (*OFget >(&getImagePixel())); + } + else + { + result = SetImagePixelModuleVisitor(rows, cols,bitsAlloc, bitsStored, highBit, samplesPixel, colorModel) + (*OFget >(&getImagePixel())); + } + } /* Write segments */ OFVector segmentItems; if (result.good()) { - OFVector::iterator it = m_Segments.begin(); + OFMap::iterator it = m_Segments.begin(); dataset.findAndDeleteElement(DCM_SegmentSequence); for (Uint16 itemCount = 0; (it != m_Segments.end()) && result.good(); itemCount++) { @@ -1145,11 +1568,11 @@ OFCondition DcmSegmentation::writeSegmentationImageModule(DcmItem& dataset) dataset.findOrCreateSequenceItem(DCM_SegmentSequence, segmentItem, itemCount); if (segmentItem) { - result = (*it)->write(*segmentItem); + result = (*it).second->write(*segmentItem); /* Insert segment number for the segment, starting from 1 and increasing monotonically. */ if (result.good()) { - segmentItem->putAndInsertUint16(DCM_SegmentNumber, itemCount + 1); + segmentItem->putAndInsertUint16(DCM_SegmentNumber, (*it).first); } } else @@ -1172,7 +1595,7 @@ void DcmSegmentation::clearData() m_FG.clearData(); m_FGInterface.clear(); DcmIODUtil::freeContainer(m_Frames); - DcmIODUtil::freeContainer(m_Segments); + DcmIODUtil::freeMap(m_Segments); m_MaximumFractionalValue.clear(); m_SegmentationFractionalType = DcmSegTypes::SFT_UNKNOWN; m_SegmentationType = DcmSegTypes::ST_UNKNOWN; @@ -1181,6 +1604,7 @@ void DcmSegmentation::clearData() OFBool DcmSegmentation::checkPixDataLength(DcmElement* pixelData, const Uint16 rows, const Uint16 cols, + const Uint16 bytesPerPixel, const Uint32& numberOfFrames) { // Get actual length of pixel data in bytes @@ -1188,7 +1612,7 @@ OFBool DcmSegmentation::checkPixDataLength(DcmElement* pixelData, // Find out how many bytes are needed size_t bytesRequired = 0; - OFCondition result = getTotalBytesRequired(rows, cols, numberOfFrames, bytesRequired); + OFCondition result = getTotalBytesRequired(rows, cols, bytesPerPixel, numberOfFrames, bytesRequired); if (result.bad()) return OFFalse; // Length found in Pixel Data element is always even @@ -1216,19 +1640,27 @@ OFBool DcmSegmentation::checkPixDataLength(DcmElement* pixelData, OFCondition DcmSegmentation::getTotalBytesRequired(const Uint16& rows, const Uint16& cols, + const Uint16& bytesPerPixel, const Uint32& numberOfFrames, size_t& bytesRequired) { + // Compute space needed for all frames, first assume 1 byte per pixel (we adapt later for binary segmentations) + // Rows * Cols = num pixels per frame OFBool ok = OFStandard::safeMult(OFstatic_cast(size_t, rows), OFstatic_cast(size_t, cols), bytesRequired); + // Pixels per frame * num frames = total num pixels if (ok) OFStandard::safeMult(bytesRequired, OFstatic_cast(size_t, numberOfFrames), bytesRequired); + // In case of 16 bit pixel data, we need twice as much space + if (ok && (bytesPerPixel == 2)) + OFStandard::safeMult(bytesRequired, OFstatic_cast(size_t, 2), bytesRequired); if (!ok) { DCMSEG_ERROR("Cannot compute number of bytes required for Pixel Data since size_t type is too small"); return EC_TooManyBytesRequested; } - /* for binary, we only need one bit per pixel */ + // Until this point we assumed bytes per pixel = 1. Now we need to adjust this + // for binary segmentation objects.with 1 bit per pixel. size_t remainder = 0; if (m_SegmentationType == DcmSegTypes::ST_BINARY) { @@ -1245,10 +1677,10 @@ OFCondition DcmSegmentation::getTotalBytesRequired(const Uint16& rows, return EC_Normal; } -OFCondition DcmSegmentation::loadFile(DcmFileFormat& dcmff, const OFString& filename, DcmDataset*& dset) +OFCondition DcmSegmentation::loadFile(DcmFileFormat& dcmff, const OFString& filename, DcmDataset*& dset, const E_TransferSyntax xfer) { dset = NULL; - OFCondition result = dcmff.loadFile(filename.c_str()); + OFCondition result = dcmff.loadFile(filename.c_str(), xfer); if (result.bad()) { DCMSEG_ERROR("Could not load file " << filename << ": " << result.text()); @@ -1263,6 +1695,7 @@ OFCondition DcmSegmentation::loadFile(DcmFileFormat& dcmff, const OFString& file return result; } + OFCondition DcmSegmentation::readSegmentationFractionalType(DcmItem& item) { m_SegmentationFractionalType = DcmSegTypes::SFT_UNKNOWN; @@ -1272,7 +1705,7 @@ OFCondition DcmSegmentation::readSegmentationFractionalType(DcmItem& item) } DcmCodeString element(DCM_SegmentationFractionalType); OFCondition result = DcmIODUtil::getAndCheckElementFromDataset( - item, element, getRules()->getByTag(DCM_SegmentationFractionalType)); + item, element, DcmSegmentation::IODImage::getRules()->getByTag(DCM_SegmentationFractionalType)); OFString str; if (result.good()) { @@ -1298,8 +1731,8 @@ OFCondition DcmSegmentation::readSegmentationType(DcmItem& item) } DcmCodeString element(DCM_SegmentationType); - OFCondition result - = DcmIODUtil::getAndCheckElementFromDataset(item, element, getRules()->getByTag(DCM_SegmentationType)); + OFCondition result = DcmIODUtil::getAndCheckElementFromDataset( + item, element, DcmSegmentation::IODImage::getRules()->getByTag(DCM_SegmentationType)); OFString str; if (result.good()) { @@ -1316,11 +1749,6 @@ OFCondition DcmSegmentation::readSegmentationType(DcmItem& item) return result; } -// protected override of public base class function -IODImagePixelModule& DcmSegmentation::getImagePixel() -{ - return *OFget >(&DcmSegmentation::IODImage::getImagePixel()); -} OFBool DcmSegmentation::check(const OFBool checkFGStructure) { @@ -1339,7 +1767,10 @@ OFBool DcmSegmentation::check(const OFBool checkFGStructure) DCMSEG_ERROR("Too many segments defined"); return OFFalse; } - if (m_Segments.size() > m_Frames.size()) + // Check that all segments are referenced by at least one frame. + // This is not required for label maps, since frames are only indirectly (through their pixel values) + // referencing segments. + if ( (m_Segments.size() > m_Frames.size()) && (m_SegmentationType != DcmSegTypes::ST_LABELMAP) ) { DCMSEG_ERROR("More segments than frames defined"); return OFFalse; @@ -1367,7 +1798,7 @@ OFBool DcmSegmentation::check(const OFBool checkFGStructure) frameOfRefRequired = OFFalse; } OFString frameOfRef; - getFrameOfReference().getFrameOfReferenceUID(frameOfRef); + DcmSegmentation::IODImage::getFrameOfReference().getFrameOfReferenceUID(frameOfRef); if (frameOfRefRequired && frameOfRef.empty()) { DCMSEG_ERROR("Frame of Reference UID is not set for Segmentation but is required"); @@ -1443,3 +1874,78 @@ OFCondition DcmSegmentation::decompress(DcmDataset& dset) return result; } + +OFString DcmSegmentation::determineColorModel() +{ + OFString colorModel; + if (m_SegmentationType == DcmSegTypes::ST_LABELMAP) + { + colorModel = DcmSegTypes::labelmapColorModel2OFString(m_LabelmapColorModel, "MONOCHROME2"); + } + else + { + colorModel = "MONOCHROME2"; + } + return colorModel; +} + + +OFBool DcmSegmentation::checkColorModel(const OFString& photometricInterpretation) +{ + // For labelmaps, MONOCHROME2 and PALETTE is permitted + if (m_SegmentationType == DcmSegTypes::ST_LABELMAP) + { + if ((photometricInterpretation != "MONOCHROME2") && (photometricInterpretation != "PALETTE COLOR")) + { + DCMSEG_WARN("Photometric Interpretation is not set correctly (" << photometricInterpretation << "): Must be MONOCHROME2 or PALETTE COLOR for labelmaps"); + m_LabelmapColorModel = DcmSegTypes::SLCM_UNKNOWN; + return OFFalse; + } + m_LabelmapColorModel = DcmSegTypes::OFString2LabelmapColorModel(photometricInterpretation); + } + else + { + if (photometricInterpretation != "MONOCHROME2") + { + DCMSEG_WARN("Photometric Interpretation is not set correctly (" << photometricInterpretation << "): Must be MONOCHROME2 for binary and fractional segmentations"); + return OFFalse; + } + m_LabelmapColorModel = DcmSegTypes::SLCM_UNKNOWN; // not used for binary/fractional segmentations + } + // If we found PALETTE COLOR, check whether the Palette Color Lookup Table is present + if (m_LabelmapColorModel == DcmSegTypes::SLCM_PALETTE) + { + if (getPaletteColorLUT().numBits() == 0) // checks at least basic descriptor availability + { + DCMSEG_WARN("Photometric Interpretation is set to PALETTE COLOR but no or invalid Palette Color Lookup Table is present"); + return OFFalse; + } + } + return OFTrue; +} + + +void DcmSegmentation::setSOPClassUIDBasedOnSegmentationType() +{ + switch (m_SegmentationType) + { + case DcmSegTypes::ST_LABELMAP: + DcmSegmentation::IODImage::getSOPCommon().setSOPClassUID(UID_LabelMapSegmentationStorage); + break; + case DcmSegTypes::ST_FRACTIONAL: + case DcmSegTypes::ST_BINARY: + DcmSegmentation::IODImage::getSOPCommon().setSOPClassUID(UID_SegmentationStorage); + break; + case DcmSegTypes::ST_UNKNOWN: + // Print warning if segmentation type is unknown + DCMSEG_WARN("Segmentation type is unknown, setting SOP Class UID to Segmentation Storage SOP Class"); + DcmSegmentation::IODImage::getSOPCommon().setSOPClassUID(UID_SegmentationStorage); + break; + } +} + +// Explicit instantiations +template OFCondition DcmSegmentation::addFrame(Uint8*, const Uint16, const OFVector&); +template OFCondition DcmSegmentation::addFrame(Uint16*, const Uint16, const OFVector&); +template OFCondition DcmSegmentation::addFrame(Uint8* pixData); +template OFCondition DcmSegmentation::addFrame(Uint16* pixData); diff --git a/dcmseg/libsrc/segment.cc b/dcmseg/libsrc/segment.cc index f7741487..0b547745 100644 --- a/dcmseg/libsrc/segment.cc +++ b/dcmseg/libsrc/segment.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2022, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -21,10 +21,12 @@ #include "dcmtk/config/osconfig.h" +#include "dcmtk/dcmdata/dcdeftag.h" #include "dcmtk/dcmiod/iodutil.h" #include "dcmtk/dcmseg/segdoc.h" #include "dcmtk/dcmseg/segment.h" #include "dcmtk/dcmseg/segtypes.h" +#include "dcmtk/ofstd/oftypes.h" OFCondition DcmSegment::create(DcmSegment*& segment, const OFString& segmentLabel, @@ -66,13 +68,59 @@ OFCondition DcmSegment::create(DcmSegment*& segment, return result; } + + +DcmSegment* DcmSegment::clone(DcmSegmentation* seg) +{ + DcmSegment* newSegment = new DcmSegment(*this); + if (newSegment != NULL) + { + if (seg != NULL) + newSegment->m_SegmentationDoc = seg; + // else: keep reference to same DcmSegmentation document + } + return newSegment; +} + +DcmSegment::DcmSegment(const DcmSegment& rhs) + : m_SegmentationDoc(rhs.m_SegmentationDoc) + , m_SegmentNumber(rhs.m_SegmentNumber) + , m_SegmentDescription(rhs.m_SegmentDescription) + , m_SegmentAlgorithmName(rhs.m_SegmentAlgorithmName) + , m_SegmentationAlgorithmIdentification(rhs.m_SegmentationAlgorithmIdentification) + , m_RecommendedDisplayGrayscaleValue(rhs.m_RecommendedDisplayGrayscaleValue) + , m_RecommendedDisplayCIELabValue(rhs.m_RecommendedDisplayCIELabValue) + , m_TrackingID(rhs.m_TrackingID) + , m_TrackingUID(rhs.m_TrackingUID) + , m_Rules(rhs.m_Rules->clone()) +{ +} + +DcmSegment& DcmSegment::operator=(const DcmSegment& rhs) +{ + if (this != &rhs) + { + m_SegmentationDoc = rhs.m_SegmentationDoc; + m_SegmentNumber = rhs.m_SegmentNumber; + m_SegmentDescription = rhs.m_SegmentDescription; + m_SegmentAlgorithmName = rhs.m_SegmentAlgorithmName; + m_SegmentationAlgorithmIdentification = rhs.m_SegmentationAlgorithmIdentification; + m_RecommendedDisplayGrayscaleValue = rhs.m_RecommendedDisplayGrayscaleValue; + m_RecommendedDisplayCIELabValue = rhs.m_RecommendedDisplayCIELabValue; + m_TrackingID = rhs.m_TrackingID; + m_TrackingUID = rhs.m_TrackingUID; + m_Rules.reset(rhs.m_Rules->clone()); + } + return *this; +} + OFCondition DcmSegment::read(DcmItem& item, const OFBool clearOldData) { if (clearOldData) clearData(); m_SegmentDescription.read(item); - DcmIODUtil::getAndCheckElementFromDataset(item, m_SegmentAlgorithmName, m_Rules.getByTag(DCM_SegmentAlgorithmName)); + DcmIODUtil::getAndCheckElementFromDataset(item, m_SegmentAlgorithmName, m_Rules->getByTag(DCM_SegmentAlgorithmName)); DcmIODUtil::readSingleItem(item, DCM_SegmentationAlgorithmIdentificationSequence, @@ -81,11 +129,12 @@ OFCondition DcmSegment::read(DcmItem& item, const OFBool clearOldData) "Segmentation Image Module"); DcmIODUtil::getAndCheckElementFromDataset( - item, m_RecommendedDisplayGrayscaleValue, m_Rules.getByTag(DCM_RecommendedDisplayGrayscaleValue)); + item, m_RecommendedDisplayGrayscaleValue, m_Rules->getByTag(DCM_RecommendedDisplayGrayscaleValue)); DcmIODUtil::getAndCheckElementFromDataset( - item, m_RecommendedDisplayCIELabValue, m_Rules.getByTag(DCM_RecommendedDisplayCIELabValue)); - DcmIODUtil::getAndCheckElementFromDataset(item, m_TrackingID, m_Rules.getByTag(DCM_TrackingID)); - DcmIODUtil::getAndCheckElementFromDataset(item, m_TrackingUID, m_Rules.getByTag(DCM_TrackingUID)); + item, m_RecommendedDisplayCIELabValue, m_Rules->getByTag(DCM_RecommendedDisplayCIELabValue)); + DcmIODUtil::getAndCheckElementFromDataset(item, m_TrackingID, m_Rules->getByTag(DCM_TrackingID)); + DcmIODUtil::getAndCheckElementFromDataset(item, m_TrackingUID, m_Rules->getByTag(DCM_TrackingUID)); + DcmIODUtil::getAndCheckElementFromDataset(item, m_SegmentNumber, m_Rules->getByTag(DCM_SegmentNumber)); return EC_Normal; } @@ -94,7 +143,7 @@ OFCondition DcmSegment::write(DcmItem& item) { OFCondition result; result = m_SegmentDescription.write(item); - DcmIODUtil::copyElementToDataset(result, item, m_SegmentAlgorithmName, m_Rules.getByTag(DCM_SegmentAlgorithmName)); + DcmIODUtil::copyElementToDataset(result, item, m_SegmentAlgorithmName, m_Rules->getByTag(DCM_SegmentAlgorithmName)); if (result.good() && m_SegmentationAlgorithmIdentification.check(OFTrue /* quiet */).good()) { @@ -107,17 +156,20 @@ OFCondition DcmSegment::write(DcmItem& item) } DcmIODUtil::copyElementToDataset( - result, item, m_RecommendedDisplayGrayscaleValue, m_Rules.getByTag(DCM_RecommendedDisplayGrayscaleValue)); + result, item, m_RecommendedDisplayGrayscaleValue, m_Rules->getByTag(DCM_RecommendedDisplayGrayscaleValue)); DcmIODUtil::copyElementToDataset( - result, item, m_RecommendedDisplayCIELabValue, m_Rules.getByTag(DCM_RecommendedDisplayCIELabValue)); - DcmIODUtil::copyElementToDataset(result, item, m_TrackingID, m_Rules.getByTag(DCM_TrackingID)); - DcmIODUtil::copyElementToDataset(result, item, m_TrackingUID, m_Rules.getByTag(DCM_TrackingUID)); + result, item, m_RecommendedDisplayCIELabValue, m_Rules->getByTag(DCM_RecommendedDisplayCIELabValue)); + DcmIODUtil::copyElementToDataset(result, item, m_TrackingID, m_Rules->getByTag(DCM_TrackingID)); + DcmIODUtil::copyElementToDataset(result, item, m_TrackingUID, m_Rules->getByTag(DCM_TrackingUID)); + m_SegmentNumber.putUint16(getSegmentNumber()); + DcmIODUtil::copyElementToDataset(result, item, m_SegmentNumber, m_Rules->getByTag(DCM_SegmentNumber)); return result; } void DcmSegment::clearData() { + m_SegmentNumber.clear(); m_SegmentDescription.clearData(); m_SegmentAlgorithmName.clear(); m_SegmentationAlgorithmIdentification.clearData(); @@ -133,8 +185,10 @@ DcmSegment::~DcmSegment() } // protected default constructor + DcmSegment::DcmSegment() : m_SegmentationDoc(NULL) + , m_SegmentNumber(DCM_SegmentNumber) , m_SegmentDescription() , m_SegmentAlgorithmName(DCM_SegmentAlgorithmName) , m_SegmentationAlgorithmIdentification() @@ -142,23 +196,25 @@ DcmSegment::DcmSegment() , m_RecommendedDisplayCIELabValue(DCM_RecommendedDisplayCIELabValue) , m_TrackingID(DCM_TrackingID) , m_TrackingUID(DCM_TrackingUID) - , m_Rules() + , m_Rules(new IODRules()) { initIODRules(); } void DcmSegment::initIODRules() { - m_Rules.addRule(new IODRule(DCM_SegmentAlgorithmName, "1", "1C", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), + m_Rules->addRule(new IODRule(DCM_SegmentNumber, "1", "1", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), OFTrue); - m_Rules.addRule( + m_Rules->addRule(new IODRule(DCM_SegmentAlgorithmName, "1", "1C", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), + OFTrue); + m_Rules->addRule( new IODRule(DCM_RecommendedDisplayGrayscaleValue, "1", "3", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), OFTrue); - m_Rules.addRule( + m_Rules->addRule( new IODRule(DCM_RecommendedDisplayCIELabValue, "3", "3", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), OFTrue); - m_Rules.addRule(new IODRule(DCM_TrackingID, "1", "1C", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), OFTrue); - m_Rules.addRule(new IODRule(DCM_TrackingUID, "1", "1C", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), OFTrue); + m_Rules->addRule(new IODRule(DCM_TrackingID, "1", "1C", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), OFTrue); + m_Rules->addRule(new IODRule(DCM_TrackingUID, "1", "1C", "SegmentationImageModule", DcmIODTypes::IE_IMAGE), OFTrue); } // -------------- getters -------------------- @@ -254,6 +310,25 @@ OFCondition DcmSegment::getTrackingUID(OFString& value, const signed long pos) return DcmIODUtil::getStringValueFromElement(m_TrackingUID, value, pos); } +Uint16 DcmSegment::getSegmentNumberRead() +{ + OFVector values; + DcmIODUtil::getUint16ValuesFromElement(m_SegmentNumber, values); + if (values.size() >0 ) + { + return values[0]; + } + else + { + return 0; + } +} + +OFshared_ptr DcmSegment::getIODRules() +{ + return m_Rules; +} + // -------------- setters -------------------- OFCondition DcmSegment::setSegmentLabel(const OFString& value, const OFBool checkValue) diff --git a/dcmseg/libsrc/segtypes.cc b/dcmseg/libsrc/segtypes.cc index 60adc798..059c8df0 100644 --- a/dcmseg/libsrc/segtypes.cc +++ b/dcmseg/libsrc/segtypes.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2022, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -39,6 +39,15 @@ makeOFConditionConst(SG_EC_UnknownSegmentationType, OFM_dcmseg, 3, OF_error, "Un makeOFConditionConst(SG_EC_InvalidValue, OFM_dcmseg, 4, OF_error, "Invalid value for Segmentation SOP Class"); makeOFConditionConst(SG_EC_NotEnoughData, OFM_dcmseg, 5, OF_error, "Not enough data"); makeOFConditionConst(SG_EC_MaxFramesReached, OFM_dcmseg, 6, OF_error, "Maximum Number of Frames reached"); +makeOFConditionConst(SG_EC_InvalidBitDepth, OFM_dcmseg, 7, OF_error, "Invalid bit depth for given Segmentation Type"); +makeOFConditionConst(SG_EC_FramesNotParallel, OFM_dcmseg, 8, OF_error, "Frames are not parallel"); +makeOFConditionConst(SG_EC_NoSegmentationBasedSOPClass, OFM_dcmseg, 9, OF_error, "No segmentation SOP class (Segmentation or Label Map Segmentation SOP Class)"); +makeOFConditionConst(SG_EC_NoConversionRequired, OFM_dcmseg, 10, OF_ok, "Segmentation-based object does not require requested conversion"); +makeOFConditionConst(SG_EC_AlreadyLabelMap, OFM_dcmseg, 11, OF_error, "Segmentation-based object is already a label map"); +makeOFConditionConst(SG_EC_CannotConvertFractionalToLabelmap, OFM_dcmseg, 12, OF_error, "Cannot convert fractional to label map segmentations"); +makeOFConditionConst(SG_EC_OverlappingSegments, OFM_dcmseg, 13, OF_error, "Binary segmentation contains overlapping segments"); +makeOFConditionConst(SG_EC_CannotConvertMissingCIELab, OFM_dcmseg, 14, OF_error, "Cannot convert to PALETTE color model since not all segments contain Recommended Display CIELab Value Macro"); +makeOFConditionConst(SG_EC_MissingPlanePositionPatient, OFM_dcmseg, 15, OF_error, "Missing Plane Position (Patient) Functional Group"); DcmSegTypes::E_SegmentationType DcmSegTypes::OFString2Segtype(const OFString& value) { @@ -46,6 +55,8 @@ DcmSegTypes::E_SegmentationType DcmSegTypes::OFString2Segtype(const OFString& va return DcmSegTypes::ST_BINARY; if (value == "FRACTIONAL") return DcmSegTypes::ST_FRACTIONAL; + if (value == "LABELMAP") + return DcmSegTypes::ST_LABELMAP; else return DcmSegTypes::ST_UNKNOWN; } @@ -58,6 +69,8 @@ OFString DcmSegTypes::segtype2OFString(const DcmSegTypes::E_SegmentationType& va return "BINARY"; case DcmSegTypes::ST_FRACTIONAL: return "FRACTIONAL"; + case DcmSegTypes::ST_LABELMAP: + return "LABELMAP"; case DcmSegTypes::ST_UNKNOWN: return "UNKNOWN"; default: @@ -105,6 +118,40 @@ DcmSegTypes::E_SegmentAlgoType DcmSegTypes::OFString2AlgoType(const OFString& al return DcmSegTypes::SAT_UNKNOWN; } + +OFString DcmSegTypes::labelmapColorModel2OFString(const DcmSegTypes::E_SegmentationLabelmapColorModel value, const OFString& fallbackValue) +{ + OFString result; + switch (value) + { + case DcmSegTypes::SLCM_MONOCHROME2: + return "MONOCHROME2"; + case DcmSegTypes::SLCM_PALETTE: + return "PALETTE COLOR"; + case DcmSegTypes::SLCM_UNKNOWN: + result = "UNKNOWN"; + default: + result = "Invalid labelmap color model (internal error)"; + } + if (!fallbackValue.empty()) + { + DCMSEG_WARN("Invalid value for label map color model: " << result << ". Using fallback value: " << fallbackValue); + result = fallbackValue; + } + return result; +} + + +DcmSegTypes::E_SegmentationLabelmapColorModel DcmSegTypes::OFString2LabelmapColorModel(const OFString& value) +{ + if (value == "MONOCHROME2") + return DcmSegTypes::SLCM_MONOCHROME2; + if (value == "PALETTE COLOR") + return DcmSegTypes::SLCM_PALETTE; + else + return DcmSegTypes::SLCM_UNKNOWN; +} + SegmentDescriptionMacro::SegmentDescriptionMacro() : m_SegmentLabel(DCM_SegmentLabel) , m_SegmentDescription(DCM_SegmentDescription) @@ -119,6 +166,27 @@ SegmentDescriptionMacro::~SegmentDescriptionMacro() { } +SegmentDescriptionMacro& SegmentDescriptionMacro::operator=(const SegmentDescriptionMacro& rhs) +{ + if (this != &rhs) + { + m_SegmentLabel = rhs.m_SegmentLabel; + m_SegmentDescription = rhs.m_SegmentDescription; + m_SegmentAlgorithmType = rhs.m_SegmentAlgorithmType; + m_GeneralAnatomyCode = rhs.m_GeneralAnatomyCode; + m_SegmentedPropertyCategoryCode = rhs.m_SegmentedPropertyCategoryCode; + m_SegmentedPropertyType = rhs.m_SegmentedPropertyType; + } + return *this; +} + + +SegmentDescriptionMacro* SegmentDescriptionMacro::clone() +{ + return new SegmentDescriptionMacro(*this); +} + + void SegmentDescriptionMacro::clearData() { m_SegmentLabel.clear(); @@ -270,6 +338,28 @@ SegmentedPropertyTypeCodeItem::SegmentedPropertyTypeCodeItem() { } +SegmentedPropertyTypeCodeItem& SegmentedPropertyTypeCodeItem::operator=(const SegmentedPropertyTypeCodeItem& rhs) +{ + if (this != &rhs) + { + m_SegmentedPropertyTypeCode = rhs.m_SegmentedPropertyTypeCode; + DcmIODUtil::copyContainer(rhs.m_SegmentedPropertyTypeModifierCode, m_SegmentedPropertyTypeModifierCode); + } + return *this; +} + +SegmentedPropertyTypeCodeItem* SegmentedPropertyTypeCodeItem::clone() +{ + return new SegmentedPropertyTypeCodeItem(*this); +} + +SegmentedPropertyTypeCodeItem::SegmentedPropertyTypeCodeItem(const SegmentedPropertyTypeCodeItem& rhs) + : m_SegmentedPropertyTypeCode(rhs.m_SegmentedPropertyTypeCode) + , m_SegmentedPropertyTypeModifierCode() +{ + DcmIODUtil::copyContainer(rhs.m_SegmentedPropertyTypeModifierCode, m_SegmentedPropertyTypeModifierCode); +} + SegmentedPropertyTypeCodeItem::~SegmentedPropertyTypeCodeItem() { DcmIODUtil::freeContainer(m_SegmentedPropertyTypeModifierCode); diff --git a/dcmseg/libsrc/segutils.cc b/dcmseg/libsrc/segutils.cc index 3b8b5462..6fa25a9f 100644 --- a/dcmseg/libsrc/segutils.cc +++ b/dcmseg/libsrc/segutils.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, Open Connections GmbH + * Copyright (C) 2015-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -23,60 +23,11 @@ #include "dcmtk/dcmiod/iodtypes.h" #include "dcmtk/dcmseg/segtypes.h" +#include #include "dcmtk/dcmseg/segutils.h" -DcmIODTypes::Frame* DcmSegUtils::packBinaryFrame(const Uint8* pixelData, const Uint16 rows, const Uint16 columns) -{ - // Sanity checking - const size_t totalBits = OFstatic_cast(size_t, rows) * columns; - if (totalBits == 0) - { - DCMSEG_ERROR("Unable to pack binary segmentation frame: Rows or Columns is 0"); - return NULL; - } - if (!pixelData) - { - DCMSEG_ERROR("Unable to pack binary segmentation frame: No pixel data provided"); - return NULL; - } - - // Calculate total number of bytes required - size_t totalBytes = (totalBits + 7) / 8; // +7 to round up to the nearest byte - - // Allocate memory for the packed bit array - Uint8* packedData = new Uint8[totalBytes]; - if (packedData == NULL) - { - DCMSEG_ERROR("Cannot allocate memory for packed binary frame"); - return NULL; - } - memset(packedData, 0, totalBytes); // Initialize to 0 - - // Pack the bits - for (Uint32 i = 0; i < totalBits; ++i) { - if (pixelData[i] != 0) { - Uint32 byteIndex = i / 8; - Uint32 bitIndex = i % 8; - DCMSEG_TRACE("bitIndex: " << bitIndex << ", byteIndex: " << byteIndex << ", packedData[byteIndex]: " << DcmSegUtils::debugByte2Bin(packedData[byteIndex])); - packedData[byteIndex] |= (1 << bitIndex); // Fill from right to left - } - } - - // Create and return the frame - DcmIODTypes::Frame* frame = new DcmIODTypes::Frame(); - if (frame == NULL) - { - DCMSEG_ERROR("Cannot allocate memory for packed binary frame"); - delete[] packedData; - return NULL; - } - frame->pixData = packedData; - frame->length = totalBytes; - return frame; -} - -OFCondition DcmSegUtils::concatBinaryFrames(const OFVector& frames, +OFCondition DcmSegUtils::concatBinaryFrames(const OFVector& frames, const Uint16 rows, const Uint16 cols, Uint8* pixData, @@ -99,25 +50,47 @@ OFCondition DcmSegUtils::concatBinaryFrames(const OFVector& } // Initialize the target pixData array to 0 - std::memset(pixData, 0, pixDataLength); + memset(pixData, 0, pixDataLength); // Concatenate the bits - Uint32 bitIndex = 0; + size_t bitIndex = 0; + for (size_t frameIndex = 0; frameIndex < frames.size(); ++frameIndex) { - const DcmIODTypes::Frame* frame = frames[frameIndex]; - Uint32 frameBits = rows * cols; - for (Uint32 i = 0; i < frameBits; ++i) + // Make sure frame has correct bit depth + if (frames[frameIndex]->bytesPerPixel() != 1) { - Uint32 byteIndex = i / 8; - Uint32 bitPos = i % 8; - if (frame->pixData[byteIndex] & (1 << bitPos % 8)) { - Uint32 targetByteIndex = bitIndex / 8; - Uint32 targetBitPos = bitIndex % 8; - pixData[targetByteIndex] |= (1 << targetBitPos); - DCMSEG_TRACE("Setting bit at targetByteIndex: " << targetByteIndex << ", targetBitPos: " << targetBitPos << ", frame->pixData[" << byteIndex << "]: " << DcmSegUtils::debugByte2Bin(frame->pixData[byteIndex]) << ", value: " << DcmSegUtils::debugByte2Bin(pixData[targetByteIndex])); + DCMSEG_ERROR("Cannot concatenate frames with bits allocated != 1"); + return EC_IllegalParameter; + } + // Cast the frame to the appropriate type to make access easier + DcmIODTypes::Frame* frame = OFstatic_cast(DcmIODTypes::Frame*,frames[frameIndex]); + size_t frameBits = rows * cols; + // If a frame always has bytes fully packed, i.e. number of bits i a multiple of 8, + // we can copy the bytes directly and don't have to fiddle with the bits + if (frameBits % 8 == 0) + { + size_t bytesToCopy = frameBits / 8; + memcpy(pixData + (bitIndex / 8), frame->getPixelData(), bytesToCopy); + DCMSEG_TRACE("Copying " << bytesToCopy << " bytes from frame " << frameIndex << " to pixData at index " << (bitIndex / 8)); + bitIndex += frameBits; + } + else + { + // we need to copy bitwise, so we iterate over the bits + DCMSEG_TRACE("Copying " << frameBits << " bits from frame " << frameIndex << " to pixData at index " << (bitIndex / 8)); + for (size_t i = 0; i < frameBits; ++i) + { + size_t byteIndex = i / 8; + size_t bitPos = i % 8; + if (frame->m_pixData[byteIndex] & (1 << bitPos % 8)) { + size_t targetByteIndex = bitIndex / 8; + size_t targetBitPos = bitIndex % 8; + pixData[targetByteIndex] |= (1 << targetBitPos); + DCMSEG_TRACE("Setting bit at targetByteIndex: " << targetByteIndex << ", targetBitPos: " << targetBitPos << ", frame->pixData[" << byteIndex << "]: " << DcmSegUtils::debugByte2Bin(OFstatic_cast(Uint8*, frame->getPixelData())[byteIndex]) << ", value: " << DcmSegUtils::debugByte2Bin(pixData[targetByteIndex])); + } + bitIndex++; } - bitIndex++; } } @@ -125,7 +98,7 @@ OFCondition DcmSegUtils::concatBinaryFrames(const OFVector& } -DcmIODTypes::Frame* DcmSegUtils::unpackBinaryFrame(const DcmIODTypes::Frame* frame, Uint16 rows, Uint16 cols) +DcmIODTypes::Frame* DcmSegUtils::unpackBinaryFrame(const DcmIODTypes::Frame* frame, Uint16 rows, Uint16 cols) { // Sanity checking if ((frame == NULL) || (rows == 0) || (cols == 0)) @@ -138,34 +111,33 @@ DcmIODTypes::Frame* DcmSegUtils::unpackBinaryFrame(const DcmIODTypes::Frame* fra Uint32 totalPixels = rows * cols; // Allocate memory for the unpacked pixel data - DcmIODTypes::Frame* result = new DcmIODTypes::Frame(); + DcmIODTypes::Frame* result = new DcmIODTypes::Frame(); if (result == NULL) { DCMSEG_ERROR("Cannot allocate memory for unpacked binary frame"); return NULL; } - result->pixData = new Uint8[totalPixels]; - if (result->pixData == NULL) + result->m_pixData = new Uint8[totalPixels]; + if (result->m_pixData == NULL) { DCMSEG_ERROR("Cannot allocate memory for unpacked binary frame"); delete result; return NULL; } - result->length = totalPixels; - memset(result->pixData, 0, totalPixels); // Initialize to 0 + result->m_numPixels = totalPixels; + memset(result->m_pixData, 0, totalPixels); // Initialize to 0 // Unpack the bits for (Uint32 i = 0; i < totalPixels; ++i) { Uint32 byteIndex = i / 8; Uint32 bitIndex = i % 8; - if (frame->pixData[byteIndex] & (1 << (bitIndex))) { - result->pixData[i] = 1; + if (frame->m_pixData[byteIndex] & (1 << (bitIndex))) { + result->m_pixData[i] = 1; } else { - result->pixData[i] = 0; + result->m_pixData[i] = 0; } } - return result; } @@ -197,4 +169,3 @@ OFString DcmSegUtils::debugByte2Bin(Uint8 b) } return result; } - diff --git a/dcmseg/tests/CMakeLists.txt b/dcmseg/tests/CMakeLists.txt index 5636eb01..fb36b784 100644 --- a/dcmseg/tests/CMakeLists.txt +++ b/dcmseg/tests/CMakeLists.txt @@ -1,10 +1,11 @@ # declare executables DCMTK_ADD_TEST_EXECUTABLE(dcmseg_tests tbigdim.cc + tconcat_binary.cc tests.cc - tutils.cc + tlabelmap.cc troundtrip.cc - tconcat_binary.cc + tutils.cc ) # make sure executables are linked to the corresponding libraries diff --git a/dcmseg/tests/Makefile.dep b/dcmseg/tests/Makefile.dep index 91ef09b6..aab1cc66 100644 --- a/dcmseg/tests/Makefile.dep +++ b/dcmseg/tests/Makefile.dep @@ -76,6 +76,7 @@ tbigdim.o: tbigdim.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ @@ -145,9 +146,10 @@ tconcat_binary.o: tconcat_binary.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ ../../dcmfg/include/dcmtk/dcmfg/fgdefine.h \ - ../../dcmfg/include/dcmtk/dcmfg/concatenationloader.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../dcmfg/include/dcmtk/dcmfg/concatenationloader.h \ ../../ofstd/include/dcmtk/ofstd/ofmap.h ../include/dcmtk/dcmseg/segdoc.h \ ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ @@ -157,6 +159,8 @@ tconcat_binary.o: tconcat_binary.cc \ ../../dcmfg/include/dcmtk/dcmfg/fg.h \ ../../dcmfg/include/dcmtk/dcmfg/fgbase.h \ ../../dcmfg/include/dcmtk/dcmfg/fgtypes.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgseg.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ ../../dcmiod/include/dcmtk/dcmiod/iodimage.h \ ../../dcmiod/include/dcmtk/dcmiod/iodcommn.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ @@ -167,7 +171,6 @@ tconcat_binary.o: tconcat_binary.cc \ ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ @@ -188,10 +191,16 @@ tconcat_binary.o: tconcat_binary.cc \ ../../dcmiod/include/dcmtk/dcmiod/modenhequipment.h \ ../../dcmiod/include/dcmtk/dcmiod/modimagepixel.h \ ../../dcmiod/include/dcmtk/dcmiod/modmultiframedimension.h \ + ../../dcmiod/include/dcmtk/dcmiod/modiccprofile.h \ ../../dcmiod/include/dcmtk/dcmiod/modmultiframefg.h \ ../../dcmiod/include/dcmtk/dcmiod/modsegmentationseries.h \ - ../include/dcmtk/dcmseg/segdef.h ../include/dcmtk/dcmseg/segtypes.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpalettecolorlut.h \ + ../include/dcmtk/dcmseg/segdef.h ../include/dcmtk/dcmseg/segment.h \ + ../include/dcmtk/dcmseg/segtypes.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrst.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrui.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrut.h \ + ../include/dcmtk/dcmseg/segutils.h \ ../../ofstd/include/dcmtk/ofstd/oftest.h \ ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ @@ -253,7 +262,146 @@ tests.o: tests.cc ../../config/include/dcmtk/config/osconfig.h \ ../../oflog/include/dcmtk/oflog/logmacro.h \ ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ ../../oflog/include/dcmtk/oflog/tracelog.h -troundtrip.o: troundtrip.cc ../../config/include/dcmtk/config/osconfig.h \ +tlabelmap.o: tlabelmap.cc ../../config/include/dcmtk/config/osconfig.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ + ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpalettecolorlut.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ + ../include/dcmtk/dcmseg/segdoc.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ + ../../dcmfg/include/dcmtk/dcmfg/concatenationcreator.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgdefine.h \ + ../../dcmfg/include/dcmtk/dcmfg/concatenationloader.h \ + ../../dcmfg/include/dcmtk/dcmfg/fginterface.h \ + ../../dcmfg/include/dcmtk/dcmfg/fg.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgbase.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgtypes.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgseg.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodimage.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodcommn.h \ + ../../dcmiod/include/dcmtk/dcmiod/modcommoninstanceref.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrlo.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodreferences.h \ + ../../dcmiod/include/dcmtk/dcmiod/modequipment.h \ + ../../dcmiod/include/dcmtk/dcmiod/modfor.h \ + ../../dcmiod/include/dcmtk/dcmiod/modgeneralseries.h \ + ../../dcmiod/include/dcmtk/dcmiod/modgeneralstudy.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpatient.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpatientstudy.h \ + ../../dcmiod/include/dcmtk/dcmiod/modsopcommon.h \ + ../../dcmiod/include/dcmtk/dcmiod/modgeneralimage.h \ + ../../dcmiod/include/dcmtk/dcmiod/modimagepixelvariant.h \ + ../../dcmiod/include/dcmtk/dcmiod/modimagepixelbase.h \ + ../../ofstd/include/dcmtk/ofstd/ofvriant.h \ + ../../ofstd/include/dcmtk/ofstd/diag/stralias.def \ + ../../dcmiod/include/dcmtk/dcmiod/modenhequipment.h \ + ../../dcmiod/include/dcmtk/dcmiod/modimagepixel.h \ + ../../dcmiod/include/dcmtk/dcmiod/modmultiframedimension.h \ + ../../dcmiod/include/dcmtk/dcmiod/modiccprofile.h \ + ../../dcmiod/include/dcmtk/dcmiod/modmultiframefg.h \ + ../../dcmiod/include/dcmtk/dcmiod/modsegmentationseries.h \ + ../include/dcmtk/dcmseg/segdef.h ../include/dcmtk/dcmseg/segment.h \ + ../include/dcmtk/dcmseg/segtypes.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrst.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrui.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrut.h \ + ../include/dcmtk/dcmseg/segutils.h \ + ../../ofstd/include/dcmtk/ofstd/oftest.h \ + ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ + ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ + ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ + ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ + ../../ofstd/include/dcmtk/ofstd/ofexit.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcuid.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgfracon.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrfd.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrdt.h \ + ../../ofstd/include/dcmtk/ofstd/ofdatime.h \ + ../../ofstd/include/dcmtk/ofstd/ofdate.h \ + ../../ofstd/include/dcmtk/ofstd/oftime.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrul.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrsh.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgpixmsr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgplanor.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgplanpo.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdict.h \ + ../../dcmdata/include/dcmtk/dcmdata/dchashdi.h \ + ../../ofstd/include/dcmtk/ofstd/oftempf.h +tpacking.o: tpacking.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmseg/segdoc.h \ ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ @@ -319,14 +467,17 @@ troundtrip.o: troundtrip.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ ../../dcmfg/include/dcmtk/dcmfg/concatenationcreator.h \ ../../dcmfg/include/dcmtk/dcmfg/fgdefine.h \ - ../../dcmfg/include/dcmtk/dcmfg/concatenationloader.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../dcmfg/include/dcmtk/dcmfg/concatenationloader.h \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../dcmfg/include/dcmtk/dcmfg/fginterface.h \ ../../dcmfg/include/dcmtk/dcmfg/fg.h \ ../../dcmfg/include/dcmtk/dcmfg/fgbase.h \ ../../dcmfg/include/dcmtk/dcmfg/fgtypes.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgseg.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ ../../dcmiod/include/dcmtk/dcmiod/iodimage.h \ ../../dcmiod/include/dcmtk/dcmiod/iodcommn.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ @@ -337,7 +488,6 @@ troundtrip.o: troundtrip.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ @@ -358,13 +508,16 @@ troundtrip.o: troundtrip.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/modenhequipment.h \ ../../dcmiod/include/dcmtk/dcmiod/modimagepixel.h \ ../../dcmiod/include/dcmtk/dcmiod/modmultiframedimension.h \ + ../../dcmiod/include/dcmtk/dcmiod/modiccprofile.h \ ../../dcmiod/include/dcmtk/dcmiod/modmultiframefg.h \ ../../dcmiod/include/dcmtk/dcmiod/modsegmentationseries.h \ - ../include/dcmtk/dcmseg/segdef.h ../include/dcmtk/dcmseg/segtypes.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpalettecolorlut.h \ + ../include/dcmtk/dcmseg/segdef.h ../include/dcmtk/dcmseg/segment.h \ + ../include/dcmtk/dcmseg/segtypes.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrst.h \ - ../include/dcmtk/dcmseg/segment.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrui.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrut.h \ + ../include/dcmtk/dcmseg/segutils.h \ ../../ofstd/include/dcmtk/ofstd/oftest.h \ ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ @@ -384,34 +537,52 @@ troundtrip.o: troundtrip.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ ../../dcmfg/include/dcmtk/dcmfg/fgplanor.h \ ../../dcmfg/include/dcmtk/dcmfg/fgplanpo.h \ - ../../dcmfg/include/dcmtk/dcmfg/fgseg.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdict.h \ ../../dcmdata/include/dcmtk/dcmdata/dchashdi.h \ ../../ofstd/include/dcmtk/ofstd/oftempf.h -tutils.o: tutils.cc ../../config/include/dcmtk/config/osconfig.h \ - ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ - ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ +troundtrip.o: troundtrip.cc ../../config/include/dcmtk/config/osconfig.h \ + ../include/dcmtk/dcmseg/segdoc.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ ../../oflog/include/dcmtk/oflog/oflog.h \ ../../oflog/include/dcmtk/oflog/logger.h \ ../../oflog/include/dcmtk/oflog/config.h \ - ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ - ../../ofstd/include/dcmtk/ofstd/ofcast.h \ - ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ ../../oflog/include/dcmtk/oflog/config/defines.h \ ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ ../../oflog/include/dcmtk/oflog/loglevel.h \ ../../ofstd/include/dcmtk/ofstd/ofvector.h \ - ../../ofstd/include/dcmtk/ofstd/oftypes.h \ ../../oflog/include/dcmtk/oflog/tstring.h \ - ../../ofstd/include/dcmtk/ofstd/ofstring.h \ - ../../ofstd/include/dcmtk/ofstd/ofstream.h \ ../../oflog/include/dcmtk/oflog/tchar.h \ ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ ../../oflog/include/dcmtk/oflog/appender.h \ ../../ofstd/include/dcmtk/ofstd/ofmem.h \ ../../ofstd/include/dcmtk/ofstd/ofutil.h \ - ../../ofstd/include/dcmtk/ofstd/oftraits.h \ ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ ../../oflog/include/dcmtk/oflog/layout.h \ ../../oflog/include/dcmtk/oflog/streams.h \ @@ -423,27 +594,145 @@ tutils.o: tutils.cc ../../config/include/dcmtk/config/osconfig.h \ ../../oflog/include/dcmtk/oflog/logmacro.h \ ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ ../../oflog/include/dcmtk/oflog/tracelog.h \ - ../../ofstd/include/dcmtk/ofstd/ofcond.h \ - ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ - ../../ofstd/include/dcmtk/ofstd/diag/push.def \ - ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ - ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ + ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ + ../../dcmfg/include/dcmtk/dcmfg/concatenationcreator.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgdefine.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../dcmfg/include/dcmtk/dcmfg/concatenationloader.h \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../../dcmfg/include/dcmtk/dcmfg/fginterface.h \ + ../../dcmfg/include/dcmtk/dcmfg/fg.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgbase.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgtypes.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgseg.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodimage.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodcommn.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ + ../../dcmiod/include/dcmtk/dcmiod/modcommoninstanceref.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrlo.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ + ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodreferences.h \ + ../../dcmiod/include/dcmtk/dcmiod/modequipment.h \ + ../../dcmiod/include/dcmtk/dcmiod/modfor.h \ + ../../dcmiod/include/dcmtk/dcmiod/modgeneralseries.h \ + ../../dcmiod/include/dcmtk/dcmiod/modgeneralstudy.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpatient.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpatientstudy.h \ + ../../dcmiod/include/dcmtk/dcmiod/modsopcommon.h \ + ../../dcmiod/include/dcmtk/dcmiod/modgeneralimage.h \ + ../../dcmiod/include/dcmtk/dcmiod/modimagepixelvariant.h \ + ../../dcmiod/include/dcmtk/dcmiod/modimagepixelbase.h \ + ../../ofstd/include/dcmtk/ofstd/ofvriant.h \ + ../../ofstd/include/dcmtk/ofstd/diag/stralias.def \ + ../../dcmiod/include/dcmtk/dcmiod/modenhequipment.h \ + ../../dcmiod/include/dcmtk/dcmiod/modimagepixel.h \ + ../../dcmiod/include/dcmtk/dcmiod/modmultiframedimension.h \ + ../../dcmiod/include/dcmtk/dcmiod/modiccprofile.h \ + ../../dcmiod/include/dcmtk/dcmiod/modmultiframefg.h \ + ../../dcmiod/include/dcmtk/dcmiod/modsegmentationseries.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpalettecolorlut.h \ + ../include/dcmtk/dcmseg/segdef.h ../include/dcmtk/dcmseg/segment.h \ + ../include/dcmtk/dcmseg/segtypes.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrst.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrui.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrut.h \ + ../include/dcmtk/dcmseg/segutils.h \ + ../../ofstd/include/dcmtk/ofstd/oftest.h \ + ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ + ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ + ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ + ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ + ../../ofstd/include/dcmtk/ofstd/ofexit.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcuid.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgfracon.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrfd.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrdt.h \ + ../../ofstd/include/dcmtk/ofstd/ofdatime.h \ + ../../ofstd/include/dcmtk/ofstd/ofdate.h \ + ../../ofstd/include/dcmtk/ofstd/oftime.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrul.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrsh.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgpixmsr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgplanor.h \ + ../../dcmfg/include/dcmtk/dcmfg/fgplanpo.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdict.h \ + ../../dcmdata/include/dcmtk/dcmdata/dchashdi.h \ + ../../ofstd/include/dcmtk/ofstd/oftempf.h +tutils.o: tutils.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmseg/segutils.h ../include/dcmtk/dcmseg/segdef.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ ../include/dcmtk/dcmseg/segtypes.h \ ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlo.h \ ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ @@ -455,6 +744,9 @@ tutils.o: tutils.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/oflist.h \ ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ @@ -472,4 +764,5 @@ tutils.o: tutils.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ ../../ofstd/include/dcmtk/ofstd/ofexit.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcuid.h + ../../dcmdata/include/dcmtk/dcmdata/dcuid.h \ + ../../ofstd/include/dcmtk/ofstd/oftime.h diff --git a/dcmseg/tests/Makefile.in b/dcmseg/tests/Makefile.in index 1ec50ada..4a4dee07 100644 --- a/dcmseg/tests/Makefile.in +++ b/dcmseg/tests/Makefile.in @@ -18,16 +18,19 @@ oflogdir = $(top_srcdir)/../oflog dcmdatadir = $(top_srcdir)/../dcmdata dcmioddir = $(top_srcdir)/../dcmiod dcmfgdir = $(top_srcdir)/../dcmfg +dcmimgledir = $(top_srcdir)/../dcmimgle LIBDIRS = -L$(top_srcdir)/libsrc -L$(ofstddir)/libsrc -L$(oflogdir)/libsrc \ -L$(dcmdatadir)/libsrc -L$(dcmioddir)/libsrc -L$(dcmfgdir)/libsrc \ - -L$(oficonvdir)/libsrc -LOCALLIBS = -ldcmseg -ldcmfg -ldcmiod -ldcmdata -loflog -lofstd -loficonv $(ZLIBLIBS) \ - $(CHARCONVLIBS) $(MATHLIBS) + -L$(oficonvdir)/libsrc -L$(dcmimgledir)/libsrc + +LOCALLIBS = -ldcmseg -ldcmfg -ldcmiod -ldcmimgle -ldcmdata \ + -loflog -lofstd -loficonv $(ZLIBLIBS) $(CHARCONVLIBS) $(MATHLIBS) + LOCALINCLUDES = -I$(top_srcdir)/include -I$(ofstddir)/include -I$(oflogdir)/include \ - -I$(dcmdatadir)/include -I$(dcmioddir)/include -I$(dcmfgdir)/include \ + -I$(dcmdatadir)/include -I$(dcmioddir)/include -I$(dcmfgdir)/include -test_objs = tbigdim.o tconcat_binary.o tests.o troundtrip.o tutils.o +test_objs = tbigdim.o tconcat_binary.o tests.o tlabelmap.o troundtrip.o tutils.o objs = $(test_objs) progs = tests diff --git a/dcmseg/tests/tbigdim.cc b/dcmseg/tests/tbigdim.cc index 6098e14b..988bc83d 100644 --- a/dcmseg/tests/tbigdim.cc +++ b/dcmseg/tests/tbigdim.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2022-2024, OFFIS e.V. + * Copyright (C) 2022-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -23,6 +23,7 @@ #include "dcmtk/ofstd/oftest.h" #include "dcmtk/dcmseg/segtypes.h" /* for DCMSEG_DEBUG */ +// DCMTK's original OFMap implementation is too slow for this test... #ifdef HAVE_STL_MAP #include "dcmtk/dcmseg/segdoc.h" #include "dcmtk/dcmseg/segment.h" @@ -31,7 +32,6 @@ #include "dcmtk/dcmfg/fgpixmsr.h" #include "dcmtk/dcmfg/fgplanor.h" #include "dcmtk/dcmfg/fgplanpo.h" -#include "dcmtk/dcmfg/fgseg.h" #include "dcmtk/dcmiod/iodmacro.h" #include "dcmtk/dcmdata/dcxfer.h" #include "dcmtk/dcmdata/dcdict.h" @@ -41,11 +41,13 @@ static const Uint8 NUM_ROWS = 5; static const Uint8 NUM_COLS = 5; +static const Uint8 NUM_THREADS = 16; // Use 16 threads for writing and reading // Restrict to 1.000.000 Frames since the theoretical 2^31-1 number of frames // results in too much memory usage and waiting time + static const Uint32 NUM_FRAMES = 1000000; -static const Uint16 NUM_SEGS = DCM_SEG_MAX_SEGMENTS; +static const size_t NUM_SEGS = DCM_SEG_MAX_SEGMENTS; static const Uint8 NUM_PIXELS_PER_FRAME = NUM_COLS * NUM_ROWS; @@ -79,6 +81,7 @@ OFTEST_FLAGS(dcmseg_bigdim, EF_Slow) DcmDataset* ds = dcmff.getDataset(); seg->setCheckDimensionsOnWrite(OFFalse); seg->setCheckFGOnWrite(OFFalse); + seg->getFunctionalGroups().setUseThreads(NUM_THREADS); OFCondition result = seg->writeDataset(*ds); OFCHECK(result.good()); @@ -87,19 +90,23 @@ OFTEST_FLAGS(dcmseg_bigdim, EF_Slow) OFString temp_fn = tf.getFilename(); OFCHECK(!temp_fn.empty()); OFCHECK(dcmff.saveFile(temp_fn.c_str(), EXS_LittleEndianExplicit).good()); + tf.stealFile(); // Read object from dataset into DcmSegmentation object, write again to dataset and // check whether object after writing is identical to object after writing. // the same expected result delete seg; seg = NULL; - DcmSegmentation::loadFile(temp_fn, seg).good(); + DcmSegmentation::LoadingFlags flags; + flags.m_numThreads = NUM_THREADS; // Use 16 threads for reading + DcmSegmentation::loadFile(temp_fn, seg, flags).good(); OFCHECK(seg != OFnullptr); if (seg) { DcmDataset dset; seg->setCheckDimensionsOnWrite(OFFalse); seg->setCheckFGOnWrite(OFFalse); + seg->getFunctionalGroups().setUseThreads(NUM_THREADS); OFCHECK(seg->writeDataset(dset).good()); checkCreatedObject(dset); delete seg; @@ -192,9 +199,8 @@ static void addFrames(DcmSegmentation* seg) if (!seg) return; - FGSegmentation* fg_seg = new FGSegmentation(); FGFrameContent* fg = new FGFrameContent(); - OFCHECK(fg && fg_seg); + OFCHECK(fg); fg->setStackID("1"); if (fg) { @@ -218,16 +224,14 @@ static void addFrames(DcmSegmentation* seg) { data[i] = i; } - OFCHECK(fg_seg->setReferencedSegmentNumber(frameNo % (DCM_SEG_MAX_SEGMENTS + 1)).good()); // limit/loop to 16 bit + Uint16 segmentNumber = OFstatic_cast(Uint16, ((frameNo-1) % (NUM_SEGS)) +1); // segment numbers start at 1 OFVector perFrameFGs; perFrameFGs.push_back(fg); - perFrameFGs.push_back(fg_seg); - OFCHECK(seg->addFrame(data, frameNo % (DCM_SEG_MAX_SEGMENTS + 1), perFrameFGs).good()); + OFCHECK(seg->addFrame(data, segmentNumber, perFrameFGs).good()); delete[] data; } } delete fg; - delete fg_seg; } static void addDimensions(DcmSegmentation* seg) @@ -278,10 +282,12 @@ static void checkCreatedObject(DcmDataset& dset) OFCHECK(dset.findAndGetSequence(DCM_PerFrameFunctionalGroupsSequence, seq).good()); if (seq != NULL) { - size_t card = seq->card(); - OFCHECK(card == NUM_FRAMES); + size_t numFrames = seq->card(); + OFOStringStream oss; + oss << "Expected " << NUM_FRAMES << " frames, but got " << numFrames; + OFCHECK_MSG(numFrames == NUM_FRAMES, oss.str().c_str()); DcmItem* item = seq->getItem(0); - for (size_t n = 0; (n < card) && (item != NULL); n++) + for (size_t n = 0; (n < numFrames) && (item != NULL); n++) { DcmItem* fgItem = NULL; OFCHECK(item->findAndGetSequenceItem(DCM_SegmentIdentificationSequence, fgItem, 0).good()); @@ -289,7 +295,7 @@ static void checkCreatedObject(DcmDataset& dset) { Uint16 segNum = 0; OFCHECK(fgItem->findAndGetUint16(DCM_ReferencedSegmentNumber, segNum).good()); - OFCHECK(segNum == ((n + 1) % (DCM_SEG_MAX_SEGMENTS + 1))); + OFCHECK(segNum == ((n % NUM_SEGS) + 1)); } item = OFstatic_cast(DcmItem*, seq->nextInContainer(item)); diff --git a/dcmseg/tests/tconcat_binary.cc b/dcmseg/tests/tconcat_binary.cc index 04a25be6..148fb611 100644 --- a/dcmseg/tests/tconcat_binary.cc +++ b/dcmseg/tests/tconcat_binary.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019-2024, OFFIS e.V. + * Copyright (C) 2019-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -51,10 +51,12 @@ OFTEST(dcmseg_concat_binary) { ConcatenationCreator cc; result = cc.setCfgFramesPerInstance(NUM_FRAMES_PER_CONCAT); + OFCHECK_MSG(result.good(), "Could not configure Concatenation Frames per Instance"); if (result.good()) { seg->setCheckFGOnWrite(OFFalse); result = seg->writeConcatenation(cc); + OFCHECK_MSG(result.good(), "Could not write Concatenation"); if (result.good()) { size_t n = 0; @@ -95,7 +97,7 @@ OFTEST(dcmseg_concat_binary) { if (cl.getInfo().size() == 1) { - OFVector frames; + OFVector*> frames; DcmFileFormat dcmff; result = DcmSegmentation::loadConcatenation(cl, cl.getInfo().begin()->first, seg); if (result.good()) diff --git a/dcmseg/tests/tests.cc b/dcmseg/tests/tests.cc index 57a60a91..431345ef 100644 --- a/dcmseg/tests/tests.cc +++ b/dcmseg/tests/tests.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, OFFIS e.V. + * Copyright (C) 2015-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -30,5 +30,8 @@ OFTEST_REGISTER(dcmseg_packAndUnpackBinaryFrame); OFTEST_REGISTER(dcmseg_concatBinaryFrames); OFTEST_REGISTER(dcmseg_roundtrip); OFTEST_REGISTER(dcmseg_concat_binary); - +OFTEST_REGISTER(dcmseg_labelmap_8bit_mono2); +OFTEST_REGISTER(dcmseg_labelmap_16bit_mono2); +OFTEST_REGISTER(dcmseg_labelmap_8bit_palette); +OFTEST_REGISTER(dcmseg_labelmap_16bit_palette); OFTEST_MAIN("dcmseg") diff --git a/dcmseg/tests/tlabelmap.cc b/dcmseg/tests/tlabelmap.cc new file mode 100644 index 00000000..af0e1107 --- /dev/null +++ b/dcmseg/tests/tlabelmap.cc @@ -0,0 +1,676 @@ +/* + * + * Copyright (C) 2024-2025, Open Connections GmbH + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation are maintained by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmseg + * + * Author: Michael Onken + * + * Purpose: Test for creating, writing and reading labelmap segmentations + * + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ + +#include "dcmtk/dcmdata/dcdatset.h" +#include "dcmtk/dcmdata/dcdeftag.h" +#include "dcmtk/dcmdata/dcsequen.h" +#include "dcmtk/dcmiod/modpalettecolorlut.h" +#include "dcmtk/dcmseg/segdoc.h" +#include "dcmtk/dcmseg/segment.h" +#include "dcmtk/dcmseg/segtypes.h" +#include "dcmtk/ofstd/oftest.h" +#include "dcmtk/dcmfg/fgfracon.h" +#include "dcmtk/dcmfg/fgpixmsr.h" +#include "dcmtk/dcmfg/fgplanor.h" +#include "dcmtk/dcmfg/fgplanpo.h" +#include "dcmtk/dcmiod/iodmacro.h" +#include "dcmtk/dcmdata/dcxfer.h" +#include "dcmtk/dcmdata/dcdict.h" +#include "dcmtk/ofstd/ofmem.h" +#include "dcmtk/ofstd/oftempf.h" +#include "dcmtk/ofstd/oftest.h" +#include "dcmtk/ofstd/oftypes.h" + +// Dimensions are 510 x 257 = 65535 pixels per frame. +// This allows to count through 16 bit values from 0 to 65534 for each frame. We are not using value +// up to 65535 since that would result in 65536 values; since each value represents a segment requiring +// an entry in the Segment Sequence, we would exceed the maximum number of segments (65535) allowed in +// a segmentation object (Segment Number is of VR US). +// For 8 bit, the pixel are counted through repeatedly from 0 to 255. This does not match exactly on the +// number of pixels (510*257) per frame but should be sufficient for testing purposes. 255 is chosen +// because this will also exercise the maximum number of palette entries (256) in the palette color LUT. +static const Uint16 NUM_ROWS = 510; +static const Uint16 NUM_COLS = 257; +// Two frames to exercise multi-frame +static const Uint16 NUM_FRAMES = 2; +static const size_t NUM_PIXELS_PER_FRAME = NUM_COLS * NUM_ROWS; + + +// set to true to keep temporary files for debugging +static const OFBool keepTempFiles = OFFalse; + +// For 8 bit, pixel values 0 to 255 are permitted. +// For 16 bit, pixel values 0 to 65534 are permitted, since for labelmaps the pixel +// value represents the segment number as well, and DICOM permits a maximum segment number +// of 65535. + +// Since the number of segments in the 16 bit case is very large and generation takes +// some time, the 16 bits are marked as slow. + +static DcmSegmentation* createLabelMap(const Uint8 bits_allocated, const DcmSegTypes::E_SegmentationLabelmapColorModel cm); +static void setGenericValues(DcmSegmentation* seg); +static void addSharedFGs(DcmSegmentation* seg); +static void addPaletteAndICCProfile(DcmSegmentation* seg, const Uint8 bitsAllocated); +template +static void addFrames(DcmSegmentation* seg, const Uint8 bitsAllocated); +static void addSegments(DcmSegmentation *seg, const Uint8 bitsAllocated); +static void addDimensions(DcmSegmentation* seg); +static void checkCreatedObject(DcmDataset* dset, const Uint8 bits_allocated, const DcmSegTypes::E_SegmentationLabelmapColorModel cm); +static void checkPalette(DcmDataset* dset, const Uint8 bits_allocated); + + +// Concatenations tests: TBD +// static void writeAndCheckConcatenation(DcmSegmentation* seg, OFList& concats); +// static void loadAndCheckConcatenation(const OFList& concats); +// static void checkConcatenationInstance(size_t numInstance, DcmSegmentation* srcInstance, DcmDataset* concatInstance); + +OFTEST(dcmseg_labelmap_8bit_mono2) +{ + // Make sure data dictionary is loaded + if (!dcmDataDict.isDictionaryLoaded()) + { + OFCHECK_FAIL("no data dictionary loaded, check environment variable: " DCM_DICT_ENVIRONMENT_VARIABLE); + return; + } + + // Creation + DcmSegmentation* seg = createLabelMap(8, DcmSegTypes::SLCM_MONOCHROME2); + setGenericValues(seg); + addSharedFGs(seg); + addFrames(seg, 8); + addSegments(seg, 8); + addDimensions(seg); + + // Write to dataset and compare its dump with expected result + DcmFileFormat dcmff; + DcmDataset* created_ds = dcmff.getDataset(); + OFCHECK(seg->writeDataset(*created_ds).good()); + checkCreatedObject(created_ds, 8, DcmSegTypes::SLCM_MONOCHROME2); + + // Save to disk, and re-load to test import + OFTempFile tf; + OFString temp_fn = tf.getFilename(); + OFCHECK(!temp_fn.empty()); + OFCHECK(seg->saveFile(temp_fn.c_str(), EXS_LittleEndianExplicit).good()); + + // Read object from dataset into DcmSegmentation object, write again to dataset and + // check whether object after writing is identical to object after writing. + // the same expected result + delete seg; + seg = NULL; + DcmDataset loaded_ds; + DcmSegmentation::loadFile(temp_fn, seg).good(); + OFCHECK(seg != OFnullptr); + if (seg) + { + OFCHECK(seg->writeDataset(loaded_ds).good()); + checkCreatedObject(&loaded_ds, 8, DcmSegTypes::SLCM_MONOCHROME2); + } + if (keepTempFiles) + { + std::cout << "Keeping temporary file: " << temp_fn << " from test dcmseg_labelmap_8bit_mono2" << std::endl; + tf.stealFile(); + } + delete seg; +} + + +OFTEST_FLAGS(dcmseg_labelmap_16bit_mono2, EF_Slow) +{ + // Make sure data dictionary is loaded + if (!dcmDataDict.isDictionaryLoaded()) + { + OFCHECK_FAIL("no data dictionary loaded, check environment variable: " DCM_DICT_ENVIRONMENT_VARIABLE); + return; + } + + // Creation + DcmSegmentation* seg_object = createLabelMap(16, DcmSegTypes::SLCM_MONOCHROME2); + setGenericValues(seg_object); + addSharedFGs(seg_object); + addFrames(seg_object, 16); + addSegments(seg_object, 16); + addDimensions(seg_object); + + // Write to dataset and compare its dump with expected result + DcmFileFormat dcmff; + DcmDataset* dset_created = dcmff.getDataset(); + OFCHECK(seg_object->writeDataset(*dset_created).good()); + checkCreatedObject(dset_created, 16, DcmSegTypes::SLCM_MONOCHROME2); + + // Save to disk, and re-load to test import + OFTempFile tf; + OFString temp_fn = tf.getFilename(); + OFCHECK(!temp_fn.empty()); + OFCHECK(seg_object->saveFile(temp_fn.c_str(), EXS_LittleEndianExplicit).good()); + + // Read object from dataset into DcmSegmentation object, write again to dataset and + // check whether object after writing is identical to object after writing. + // the same expected result + delete seg_object; + seg_object = NULL; + DcmSegmentation::loadFile(temp_fn, seg_object).good(); + OFCHECK(seg_object != OFnullptr); + DcmDataset dset_loaded; + if (seg_object) + { + OFCHECK(seg_object->writeDataset(dset_loaded).good()); + checkCreatedObject(&dset_loaded, 16, DcmSegTypes::SLCM_MONOCHROME2); + } + + delete seg_object; + if (keepTempFiles) + { + std::cout << "Keeping temporary file: " << temp_fn << " from test dcmseg_labelmap_16bit_mono2" << std::endl; + tf.stealFile(); + } +} + +OFTEST(dcmseg_labelmap_8bit_palette) +{ + // Make sure data dictionary is loaded + if (!dcmDataDict.isDictionaryLoaded()) + { + OFCHECK_FAIL("no data dictionary loaded, check environment variable: " DCM_DICT_ENVIRONMENT_VARIABLE); + return; + } + + // Creation + DcmSegmentation* seg = createLabelMap(8, DcmSegTypes::SLCM_PALETTE); + setGenericValues(seg); + addSharedFGs(seg); + addFrames(seg, 8); + addSegments(seg, 8); + addDimensions(seg); + addPaletteAndICCProfile(seg, 8); + + // Write to dataset and compare its dump with expected result + DcmFileFormat dcmff; + DcmDataset* created_ds = dcmff.getDataset(); + OFCHECK(seg->writeDataset(*created_ds).good()); + checkCreatedObject(created_ds, 8, DcmSegTypes::SLCM_PALETTE); + + // Save to disk, and re-load to test import + OFTempFile tf; + OFString temp_fn = tf.getFilename(); + OFCHECK(!temp_fn.empty()); + OFCHECK(seg->saveFile(temp_fn.c_str(), EXS_LittleEndianExplicit).good()); + + // Read object from dataset into DcmSegmentation object, write again to dataset and + // check whether object after writing is identical to object after writing. + // the same expected result + delete seg; + seg = NULL; + DcmDataset loaded_ds; + DcmSegmentation::loadFile(temp_fn, seg).good(); + OFCHECK(seg != OFnullptr); + if (seg) + { + seg->writeDataset(loaded_ds).good(); + checkCreatedObject(&loaded_ds, 8, DcmSegTypes::SLCM_PALETTE); + } + + delete seg; + if (keepTempFiles) + { + std::cout << "Keeping temporary file: " << temp_fn << " from test dcmseg_labelmap_8bit_palette" << std::endl; + tf.stealFile(); + } +} + + +OFTEST_FLAGS(dcmseg_labelmap_16bit_palette, EF_Slow) +{ + // Make sure data dictionary is loaded + if (!dcmDataDict.isDictionaryLoaded()) + { + OFCHECK_FAIL("no data dictionary loaded, check environment variable: " DCM_DICT_ENVIRONMENT_VARIABLE); + return; + } + + // Creation + DcmSegmentation* seg_object = createLabelMap(16, DcmSegTypes::SLCM_PALETTE); + setGenericValues(seg_object); + addSharedFGs(seg_object); + addFrames(seg_object, 16); + addSegments(seg_object, 16); + addDimensions(seg_object); + addPaletteAndICCProfile(seg_object, 16); + + // Write to dataset and compare its dump with expected result + DcmFileFormat dcmff; + DcmDataset* dset_created = dcmff.getDataset(); + OFCHECK(seg_object->writeDataset(*dset_created).good()); + checkCreatedObject(dset_created, 16, DcmSegTypes::SLCM_PALETTE); + + // Save to disk, and re-load to test import + OFTempFile tf; + OFString temp_fn = tf.getFilename(); + OFCHECK(!temp_fn.empty()); + OFCHECK(seg_object->saveFile(temp_fn.c_str(), EXS_LittleEndianExplicit).good()); + + // Read object from dataset into DcmSegmentation object, write again to dataset and + // check whether object after writing is identical to object after writing. + // the same expected result + delete seg_object; + seg_object = NULL; + DcmSegmentation::loadFile(temp_fn, seg_object).good(); + OFCHECK(seg_object != OFnullptr); + DcmDataset dset_loaded; + if (seg_object) + { + seg_object->writeDataset(dset_loaded).good(); + checkCreatedObject(&dset_loaded, 16, DcmSegTypes::SLCM_PALETTE); + } + + delete seg_object; + if (keepTempFiles) + { + std::cout << "Keeping temporary file: " << temp_fn << " from test dcmseg_labelmap_16bit_palette" << std::endl; + tf.stealFile(); + } +} + + +static DcmSegmentation* createLabelMap(const Uint8 bits_allocated, const DcmSegTypes::E_SegmentationLabelmapColorModel cm) +{ + IODGeneralEquipmentModule::EquipmentInfo eq("Open Connections", "OC CT", "4711", "0.1"); + ContentIdentificationMacro ci("1", "LABEL", "DESCRIPTION", "Doe^John"); + DcmSegmentation* seg = NULL; + OFCondition result; + DcmSegmentation::createLabelmapSegmentation(seg, NUM_ROWS, NUM_COLS, eq, ci, bits_allocated == 16, cm); + OFCHECK(result.good()); + OFCHECK(seg != OFnullptr); + return seg; +} + +static void setGenericValues(DcmSegmentation* seg) +{ + if (!seg) + return; + OFCHECK(seg->getPatient().setPatientName("Bond^James").good()); + OFCHECK(seg->getPatient().setPatientID("007").good()); + OFCHECK(seg->getPatient().setPatientBirthDate("19771007").good()); + OFCHECK(seg->getStudy().setStudyDate("20190801").good()); + OFCHECK(seg->getStudy().setStudyTime("120000").good()); + OFCHECK(seg->getStudy().setStudyID("1").good()); + OFCHECK(seg->getPatientStudy().setPatientAge("040Y").good()); + OFCHECK(seg->getSeries().setSeriesDescription("Test Description").good()); + OFCHECK(seg->getSeries().setSeriesNumber("1").good()); + OFCHECK(seg->getSeries().setPatientPosition("HFS").good()); + + // Those values are usually computed automatically. UIDS are generated and date/times are set to current values. + // But in order to compare the "old" dump with the freshly created image attributes, we set some values manually, + // so that they are not overwritten with new, automatically created values later. + OFCHECK(seg->getStudy().setStudyInstanceUID("1.2.276.0.7230010.3.1.2.8323329.14863.1565940357.864811").good()); + OFCHECK(seg->getFrameOfReference().setFrameOfReferenceUID("2.25.30853397773651184949181049330553108086").good()); + OFCHECK(seg->getSeries().setSeriesInstanceUID("1.2.276.0.7230010.3.1.3.8323329.14863.1565940357.864812").good()); + OFCHECK(seg->getSOPCommon().setSOPInstanceUID("1.2.276.0.7230010.3.1.4.8323329.14863.1565940357.864813").good()); + OFCHECK(seg->getGeneralImage().setContentDate("20190927").good()); + OFCHECK(seg->getGeneralImage().setContentTime("153857").good()); + OFCHECK(seg->getGeneralImage().setContentTime("153857").good()); +} + + +static void addSharedFGs(DcmSegmentation* seg) +{ + if (!seg) + return; + + FGPixelMeasures meas; + OFCHECK(meas.setPixelSpacing("0.1\\0.1").good()); + OFCHECK(meas.setSliceThickness("1.0").good()); + OFCHECK(meas.setSpacingBetweenSlices("0.05").good()); + + FGPlanePosPatient planpo; + OFCHECK(planpo.setImagePositionPatient("0.0", "0.0", "0.0").good()); + + FGPlaneOrientationPatient planor; + OFCHECK(planor.setImageOrientationPatient("1.0", "0.0", "0.0", "0.0", "1.0", "0.0").good()); + + OFCHECK(seg->addForAllFrames(meas).good()); + OFCHECK(seg->addForAllFrames(planpo).good()); + OFCHECK(seg->addForAllFrames(planor).good()); +} + +template +static void addFrames(DcmSegmentation* seg, const Uint8 bitsAllocated) +{ + if (!seg) + return; + + FGFrameContent* fg = new FGFrameContent(); + OFCHECK(fg); + fg->setStackID("1"); + if (fg) + { + for (Uint16 frameNo = 1; frameNo <= NUM_FRAMES; frameNo++) + { + OFCHECK(fg->setFrameAcquisitionNumber(frameNo).good()); + OFCHECK(fg->setFrameReferenceDateTime("20190816092557").good()); + OFCHECK(fg->setFrameAcquisitionDateTime("20190816092557").good()); + OFCHECK(fg->setFrameAcquisitionDuration(0.001).good()); + OFCHECK(fg->setInStackPositionNumber(frameNo).good()); + OFCHECK(fg->setDimensionIndexValues(1, 0).good()); + OFCHECK(fg->setDimensionIndexValues(frameNo, 1).good()); + OFVector groups; + groups.push_back(fg); + T* data = new T[NUM_PIXELS_PER_FRAME]; + Uint32 max_value = (bitsAllocated == 16 ? 65535 : 256); + for (Uint32 i = 0; i < NUM_PIXELS_PER_FRAME; ++i) + { + data[i] = OFstatic_cast(T, (NUM_PIXELS_PER_FRAME * (frameNo - 1) + i) % max_value); + } + OFVector perFrameFGs; + perFrameFGs.push_back(fg); + OFCHECK(seg->addFrame(data, frameNo, perFrameFGs).good()); + delete[] data; + } + } + delete fg; +} + +static void addSegments(DcmSegmentation *seg, const Uint8 bitsAllocated) +{ + for (Uint32 i = 0; i < ((bitsAllocated == 16) ? 65535u : 255u); i++) + { + DcmSegment* segment = NULL; + CodeSequenceMacro category("85756007", "SCT", "Tissue"); + CodeSequenceMacro propType("51114001", "SCT", "Artery"); + + OFCHECK(DcmSegment::create(segment, "SEGLABEL", category, propType, DcmSegTypes::SAT_AUTOMATIC, "OC_DUMMY") + .good()); + OFCHECK(segment != OFnullptr); + Uint16 segno = OFstatic_cast(Uint16, i); + OFCHECK(seg->addSegment(segment, segno).good()); + } +} + + +static void addDimensions(DcmSegmentation* seg) +{ + if (!seg) + return; + IODMultiframeDimensionModule& dims = seg->getDimensions(); + OFCHECK(dims.addDimensionIndex( + DCM_StackID, "2.25.30855560781715986879861690673941231222", DCM_FrameContentSequence, "STACK_DIM") + .good()); + OFCHECK(dims.addDimensionIndex(DCM_InStackPositionNumber, + "2.25.30855560781715986879861690673941231222", + DCM_FrameContentSequence, + "STACK_DIM") + .good()); + OFunique_ptr org( + new IODMultiframeDimensionModule::DimensionOrganizationItem); + if (org) + { + org->setDimensionOrganizationUID("2.25.30855560781715986879861690673941231222"); + dims.getDimensionOrganizationSequence().push_back(org.release()); + } +} + + +static void checkCreatedObject(DcmDataset* dset, const Uint8 bits_allocated, const DcmSegTypes::E_SegmentationLabelmapColorModel cm) +{ + // Check imaging parameters + Uint16 uint16; + OFString str; + OFCHECK(dset->findAndGetUint16(DCM_BitsAllocated, uint16).good()); + OFCHECK(uint16 == bits_allocated); + OFCHECK(dset->findAndGetUint16(DCM_BitsStored, uint16).good()); + OFCHECK(uint16 == bits_allocated); + OFCHECK(dset->findAndGetUint16(DCM_HighBit, uint16).good()); + OFCHECK(uint16 == bits_allocated - 1); + OFCHECK(dset->findAndGetUint16(DCM_SamplesPerPixel, uint16).good()); + OFCHECK(uint16 == 1); + OFCHECK(dset->findAndGetUint16(DCM_PixelRepresentation, uint16).good()); + OFCHECK(uint16 == 0); + OFCHECK(dset->findAndGetOFString(DCM_PhotometricInterpretation, str).good()); + if (cm == DcmSegTypes::SLCM_MONOCHROME2) + { + OFCHECK(str == "MONOCHROME2"); + } + else + { + OFCHECK(str == "PALETTE COLOR"); + checkPalette(dset, bits_allocated); + } + Sint32 numFrames = 0; + OFCHECK(dset->findAndGetSint32(DCM_NumberOfFrames, numFrames).good()); + OFCHECK(numFrames > 0); + OFCHECK(OFstatic_cast(Uint16, numFrames) == NUM_FRAMES); + OFCHECK(dset->findAndGetUint16(DCM_Rows, uint16).good()); + OFCHECK(uint16 == NUM_ROWS); + OFCHECK(dset->findAndGetUint16(DCM_Columns, uint16).good()); + OFCHECK(uint16 == NUM_COLS); + OFCHECK(dset->findAndGetOFString(DCM_SegmentationType, str).good()); + OFCHECK(DcmSegTypes::OFString2Segtype(str) == DcmSegTypes::ST_LABELMAP); + + // Check pixel data + if (bits_allocated == 8) + { + const Uint8* pixData = NULL; + OFCHECK(dset->findAndGetUint8Array(DCM_PixelData, pixData).good()); + OFCHECK(pixData != NULL); + if (pixData) + { + Uint16 max_value = 256; + Uint32 numPixels = NUM_ROWS * NUM_COLS * NUM_FRAMES; + for (Uint32 i = 0; i < numPixels; i++) + { + OFCHECK(pixData[i] == OFstatic_cast(Uint8, i % max_value)); + } + } + // pixData contains only reference, no need to delete + } + else if (bits_allocated == 16) + { + const Uint16* pixData = NULL; + OFCHECK(dset->findAndGetUint16Array(DCM_PixelData, pixData).good()); + OFCHECK(pixData != NULL); + if (pixData) + { + for (Uint32 i = 0; i < NUM_PIXELS_PER_FRAME * numFrames; i++) + { + OFCHECK(pixData[i] == OFstatic_cast(Uint16, i % 65535)); + // report expected and actual value in case of mismatch + if (pixData[i] != OFstatic_cast(Uint16, i % 65535)) + { + DCMSEG_DEBUG("Expected at index " << i << ": " << OFstatic_cast(Uint16, i % 65535) << ", actual value: " << pixData[i]); + } + } + } + // pixData contains only reference, no need to delete + } + + // check segments + DcmSequenceOfItems* seq = NULL; + OFCHECK(dset->findAndGetSequence(DCM_SegmentSequence, seq).good()); + OFCHECK(seq != NULL); + if (seq) + { + OFCHECK(seq->card() == ((bits_allocated == 16) ? 65535u : 255u)); + } + // check that there is no Segmentation FG + DcmElement *fgSeq = NULL; + OFCHECK(dset->findAndGetElement(DCM_SegmentIdentificationSequence, fgSeq, OFTrue /* search into sub */).bad()); +} + +static void addPaletteAndICCProfile(DcmSegmentation* seg, const Uint8 bitsAllocated) +{ + if (bitsAllocated == 8) + { + IODPaletteColorLUTModule& pal8 = seg->getPaletteColorLUT(); + OFCHECK(pal8.setRedPaletteColorLookupTableDescriptor(255, 0, 8).good()); + OFCHECK(pal8.setGreenPaletteColorLookupTableDescriptor(255, 0, 8).good()); + OFCHECK(pal8.setBluePaletteColorLookupTableDescriptor(255, 0, 8).good()); + // re-use the same data for all entries + const Uint8 MAX_8BIT_ENTRIES = 255; + Uint8* data = new Uint8[MAX_8BIT_ENTRIES]; + for (Uint16 i = 0; i < MAX_8BIT_ENTRIES; i++) + { + data[i] = OFstatic_cast(Uint8, i); + } + OFCHECK(pal8.setRedPaletteColorLookupTableData(data, MAX_8BIT_ENTRIES).good()); + OFCHECK(pal8.setGreenPaletteColorLookupTableData(data, MAX_8BIT_ENTRIES).good()); + OFCHECK(pal8.setBluePaletteColorLookupTableData(data, MAX_8BIT_ENTRIES).good()); + delete [] data; + } + else if (bitsAllocated == 16) + { + IODPaletteColorLUTModule& pal16= seg->getPaletteColorLUT(); + OFCHECK(pal16.setRedPaletteColorLookupTableDescriptor(65535, 0, 16).good()); + OFCHECK(pal16.setGreenPaletteColorLookupTableDescriptor(65535, 0, 16).good()); + OFCHECK(pal16.setBluePaletteColorLookupTableDescriptor(65535, 0, 16).good()); + // re-use the same data for all entries + Uint16* data = new Uint16[65536]; + for (Uint16 i = 0; i < 65535u; i++) // TODO + { + data[i] = i; + } + OFCHECK(pal16.setRedPaletteColorLookupTableData(data, 65535).good()); + OFCHECK(pal16.setGreenPaletteColorLookupTableData(data, 65535).good()); + OFCHECK(pal16.setBluePaletteColorLookupTableData(data, 65535).good()); + delete [] data; + } + else + { + OFCHECK_FAIL("Unsupported value for bitsAllocated"); + return; + } + // Palette Color LUT Module requires ICC profile information. + // Create ICC profile dummy data which is definitely not a valid ICC profile, + // but should be sufficient for testing purposes. + const size_t ICC_LENGTH = 256; // length of ICC profile in bytes + Uint8* iccProfile = new Uint8[ICC_LENGTH]; + for (size_t i = 0; i < ICC_LENGTH; i++) + { + iccProfile[i] = OFstatic_cast(Uint8, i); + } + OFCHECK(seg->getICCProfile().setICCProfile(iccProfile, ICC_LENGTH, OFTrue /* check */).good()); + OFCHECK(seg->getICCProfile().setColorSpace("SRGB", OFTrue /* check */).good()); + delete[] iccProfile; +} + +static void checkPalette(DcmDataset* dset, const Uint8 bitsAllocated) +{ + if (bitsAllocated == 8) + { + Uint8 MAX_ENTRIES_8_BIT = 255; + IODPaletteColorLUTModule pal8; + OFCHECK(pal8.read(*dset).good()); + Uint16 numEntries, firstEntry, bits; + OFCHECK(pal8.getRedPaletteColorLookupTableDescriptor(numEntries, 0).good()); + OFCHECK(pal8.getRedPaletteColorLookupTableDescriptor(firstEntry, 1).good()); + OFCHECK(pal8.getRedPaletteColorLookupTableDescriptor(bits, 2).good()); + OFCHECK(numEntries == 255); + OFCHECK(firstEntry == 0); + OFCHECK(bits == bitsAllocated); + OFCHECK(pal8.getGreenPaletteColorLookupTableDescriptor(numEntries, 0).good()); + OFCHECK(pal8.getGreenPaletteColorLookupTableDescriptor(firstEntry, 1).good()); + OFCHECK(pal8.getGreenPaletteColorLookupTableDescriptor(bits, 2).good()); + OFCHECK(numEntries == MAX_ENTRIES_8_BIT); + OFCHECK(firstEntry == 0); + OFCHECK(bits == bitsAllocated); + OFCHECK(pal8.getBluePaletteColorLookupTableDescriptor(numEntries, 0).good()); + OFCHECK(pal8.getBluePaletteColorLookupTableDescriptor(firstEntry, 1).good()); + OFCHECK(pal8.getBluePaletteColorLookupTableDescriptor(bits, 2).good()); + OFCHECK(numEntries == MAX_ENTRIES_8_BIT); + OFCHECK(firstEntry == 0); + OFCHECK(bits == bitsAllocated); + + const Uint8* redData = NULL; + const Uint8* greenData = NULL; + const Uint8* blueData = NULL; + unsigned long numEntriesRed, numEntriesGreen, numEntriesBlue; + OFCHECK(pal8.getRedPaletteColorLookupTableData(redData, numEntriesRed).good()); + OFCHECK(pal8.getGreenPaletteColorLookupTableData(greenData, numEntriesGreen).good()); + OFCHECK(pal8.getBluePaletteColorLookupTableData(blueData, numEntriesBlue).good()); + OFCHECK(numEntriesRed == MAX_ENTRIES_8_BIT); + OFCHECK(numEntriesBlue == MAX_ENTRIES_8_BIT); + OFCHECK(numEntriesGreen == MAX_ENTRIES_8_BIT); + OFCHECK(redData != NULL); + OFCHECK(greenData != NULL); + OFCHECK(blueData != NULL); + if (redData && greenData && blueData) + { + for (Uint32 i = 0; i < MAX_ENTRIES_8_BIT; i++) + { + OFCHECK(redData[i] == OFstatic_cast(Uint8, i)); + OFCHECK(greenData[i] == OFstatic_cast(Uint8, i)); + OFCHECK(blueData[i] == OFstatic_cast(Uint8, i)); + } + } + delete[] redData; + delete[] greenData; + delete[] blueData; + } + else if (bitsAllocated == 16) + { + IODPaletteColorLUTModule pal16; + OFCHECK(pal16.read(*dset).good()); + Uint16 numEntries, firstEntry, bits; + OFCHECK(pal16.getRedPaletteColorLookupTableDescriptor(numEntries, 0).good()); + OFCHECK(pal16.getRedPaletteColorLookupTableDescriptor(firstEntry, 1).good()); + OFCHECK(pal16.getRedPaletteColorLookupTableDescriptor(bits, 2).good()); + OFCHECK(numEntries == 65535); + OFCHECK(firstEntry == 0); + OFCHECK(bits == bitsAllocated); + OFCHECK(pal16.getGreenPaletteColorLookupTableDescriptor(numEntries, 0).good()); + OFCHECK(pal16.getGreenPaletteColorLookupTableDescriptor(firstEntry, 1).good()); + OFCHECK(pal16.getGreenPaletteColorLookupTableDescriptor(bits, 2).good()); + OFCHECK(numEntries == 65535); + OFCHECK(firstEntry == 0); + OFCHECK(bits == bitsAllocated); + OFCHECK(pal16.getBluePaletteColorLookupTableDescriptor(numEntries, 0).good()); + OFCHECK(pal16.getBluePaletteColorLookupTableDescriptor(firstEntry, 1).good()); + OFCHECK(pal16.getBluePaletteColorLookupTableDescriptor(bits, 2).good()); + OFCHECK(numEntries == 65535); + OFCHECK(firstEntry == 0 ); + OFCHECK(bits == bitsAllocated); + + const Uint16* redData = NULL; + const Uint16* greenData = NULL; + const Uint16* blueData = NULL; + unsigned long numEntriesRed, numEntriesGreen, numEntriesBlue; + OFCHECK(pal16.getRedPaletteColorLookupTableData(redData, numEntriesRed).good()); + OFCHECK(pal16.getGreenPaletteColorLookupTableData(greenData, numEntriesGreen).good()); + OFCHECK(pal16.getBluePaletteColorLookupTableData(blueData, numEntriesBlue).good()); + OFCHECK(numEntriesRed == 65535); + OFCHECK(numEntriesBlue == 65535); + OFCHECK(numEntriesGreen == 65535); + OFCHECK(redData != NULL); + OFCHECK(greenData != NULL); + OFCHECK(blueData != NULL); + if (redData && greenData && blueData) + { + for (Uint32 i = 0; i < 65535; i++) + { + OFCHECK(redData[i] == i); + OFCHECK(greenData[i] == i); + OFCHECK(blueData[i] == i); + } + } + } +} + diff --git a/dcmseg/tests/tpacking.cc b/dcmseg/tests/tpacking.cc new file mode 100644 index 00000000..abde629f --- /dev/null +++ b/dcmseg/tests/tpacking.cc @@ -0,0 +1,227 @@ +/* + * + * Copyright (C) 2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmseg + * + * Author: Michael Onken + * + * Purpose: Test for packing and unpacking binary segmentation pixel data + * + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ + +#include "dcmtk/dcmseg/segdoc.h" +#include "dcmtk/dcmseg/segment.h" +#include "dcmtk/dcmseg/segutils.h" +#include "dcmtk/ofstd/oftest.h" + +#include "dcmtk/dcmfg/fgfracon.h" +#include "dcmtk/dcmfg/fgpixmsr.h" +#include "dcmtk/dcmfg/fgplanor.h" +#include "dcmtk/dcmfg/fgplanpo.h" +#include "dcmtk/dcmfg/fgseg.h" +#include "dcmtk/dcmiod/iodmacro.h" +#include "dcmtk/dcmdata/dcxfer.h" +#include "dcmtk/dcmdata/dcdict.h" +#include "dcmtk/ofstd/ofmem.h" +#include "dcmtk/ofstd/oftempf.h" +#include "dcmtk/ofstd/oftest.h" +//#include + +static const Uint16 NUM_ROWS = 512; +static const Uint16 NUM_COLS = 512; +// 17388 +static const Uint16 NUM_FRAMES = 17000; +static const size_t NUM_PIXELS_PER_FRAME = NUM_COLS * NUM_ROWS; + +static OFString EXPECTED_DUMP; + +static void prepareExpectedDump(); +static DcmSegmentation* create(); +static void setGenericValues(DcmSegmentation* seg); +static void addSharedFGs(DcmSegmentation* seg); +static void addFrames(DcmSegmentation* seg); +static void addDimensions(DcmSegmentation* seg); +static OFString write(DcmSegmentation* seg, DcmDataset& ds); + +OFTEST(dcmseg_packing) +{ + // Make sure data dictionary is loaded + if (!dcmDataDict.isDictionaryLoaded()) + { + OFCHECK_FAIL("no data dictionary loaded, check environment variable: " DCM_DICT_ENVIRONMENT_VARIABLE); + return; + } + + // Creation + std::cout << "Creating segmentation" << std::endl; + DcmSegmentation* seg = create(); + std::cout << "Setting generic values" << std::endl; + setGenericValues(seg); + std::cout << "Adding shared functional groups" << std::endl; + addSharedFGs(seg); + std::cout << "Adding frames" << std::endl; + addFrames(seg); + std::cout << "Adding dimensions" << std::endl; + addDimensions(seg); + + // Write to dataset and compare its dump with expected result + DcmFileFormat dcmff; + DcmDataset* ds = dcmff.getDataset(); + + // Save to disk, and re-load to test import + seg->getFunctionalGroups().setCheckOnWrite(OFFalse); + seg->getFunctionalGroups().setUseThreads(16); + seg->setCheckDimensionsOnWrite(OFFalse); + OFString temp_fn = "/tmp/test.dcm"; + std::cout << "Writing segmentation to file: " << temp_fn << std::endl; + OFCHECK(seg->saveFile(temp_fn.c_str(), EXS_LittleEndianExplicit).good()); + std::cout << "Temporary file created: " << temp_fn << std::endl; + delete seg; +} + +static DcmSegmentation* create() +{ + IODGeneralEquipmentModule::EquipmentInfo eq("Open Connections", "OC CT", "4711", "0.1"); + ContentIdentificationMacro ci("1", "LABEL", "DESCRIPTION", "Doe^John"); + DcmSegmentation* seg = NULL; + OFCondition result; + DcmSegmentation::createBinarySegmentation(seg, NUM_ROWS, NUM_COLS , eq, ci); + OFCHECK(result.good()); + OFCHECK(seg != OFnullptr); + return seg; +} + +static void setGenericValues(DcmSegmentation* seg) +{ + if (!seg) + return; + OFCHECK(seg->getPatient().setPatientName("Bond^James").good()); + OFCHECK(seg->getPatient().setPatientID("007").good()); + OFCHECK(seg->getPatient().setPatientBirthDate("19771007").good()); + OFCHECK(seg->getStudy().setStudyDate("20190801").good()); + OFCHECK(seg->getStudy().setStudyTime("120000").good()); + OFCHECK(seg->getStudy().setStudyID("1").good()); + OFCHECK(seg->getPatientStudy().setPatientAge("040Y").good()); + OFCHECK(seg->getSeries().setSeriesDescription("Test Description").good()); + OFCHECK(seg->getSeries().setSeriesNumber("1").good()); + OFCHECK(seg->getSeries().setPatientPosition("HFS").good()); + + // Those values are usually computed automatically. UIDS are generated and date/times are set to current values. + // But in order to compare the "old" dump with the freshly created image attributes, we set some values manually, + // so that they are not overwritten with new, automatically created values later. + OFCHECK(seg->getStudy().setStudyInstanceUID("1.2.276.0.7230010.3.1.2.8323329.14863.1565940357.864811").good()); + OFCHECK(seg->getFrameOfReference().setFrameOfReferenceUID("2.25.30853397773651184949181049330553108086").good()); + OFCHECK(seg->getSeries().setSeriesInstanceUID("1.2.276.0.7230010.3.1.3.8323329.14863.1565940357.864812").good()); + OFCHECK(seg->getSOPCommon().setSOPInstanceUID("1.2.276.0.7230010.3.1.4.8323329.14863.1565940357.864813").good()); + OFCHECK(seg->getGeneralImage().setContentDate("20190927").good()); + OFCHECK(seg->getGeneralImage().setContentTime("153857").good()); + OFCHECK(seg->getGeneralImage().setContentTime("153857").good()); +} + + +static void addSharedFGs(DcmSegmentation* seg) +{ + if (!seg) + return; + + FGPixelMeasures meas; + OFCHECK(meas.setPixelSpacing("0.1\\0.1").good()); + OFCHECK(meas.setSliceThickness("1.0").good()); + OFCHECK(meas.setSpacingBetweenSlices("0.05").good()); + + FGPlanePosPatient planpo; + OFCHECK(planpo.setImagePositionPatient("0.0", "0.0", "0.0").good()); + + FGPlaneOrientationPatient planor; + OFCHECK(planor.setImageOrientationPatient("1.0", "0.0", "0.0", "0.0", "1.0", "0.0").good()); + + OFCHECK(seg->addForAllFrames(meas).good()); + OFCHECK(seg->addForAllFrames(planpo).good()); + OFCHECK(seg->addForAllFrames(planor).good()); +} + +static void addFrames(DcmSegmentation* seg) +{ + if (!seg) + return; + + DcmSegment* segment = NULL; + CodeSequenceMacro category("85756007", "SCT", "Tissue"); + CodeSequenceMacro propType("51114001", "SCT", "Artery"); + + OFCHECK(DcmSegment::create(segment, "SEGLABEL", category, propType, DcmSegTypes::SAT_AUTOMATIC, "OC_DUMMY") + .good()); + OFCHECK(segment != OFnullptr); + Uint16 forget; + OFCHECK(seg->addSegment(segment, forget).good()); + // Segmentation FG is created automatically + FGFrameContent* fg = new FGFrameContent(); + OFCHECK(fg); + for (Uint16 frameNo = 1; frameNo <= NUM_FRAMES; frameNo++) + { + fg->setStackID("1"); + + OFCHECK(fg->setFrameAcquisitionNumber(frameNo).good()); + OFCHECK(fg->setFrameReferenceDateTime("20190816092557").good()); + OFCHECK(fg->setFrameAcquisitionDateTime("20190816092557").good()); + OFCHECK(fg->setFrameAcquisitionDuration(0.001).good()); + OFCHECK(fg->setInStackPositionNumber(frameNo).good()); + OFCHECK(fg->setDimensionIndexValues(1, 0).good()); + OFCHECK(fg->setDimensionIndexValues(frameNo, 1).good()); + OFVector groups; + groups.push_back(fg); + + Uint8* data = new Uint8[NUM_PIXELS_PER_FRAME]; + for (size_t i = 0; i < NUM_PIXELS_PER_FRAME; ++i) + { + data[i] = 1; + } + OFVector perFrameFGs; + perFrameFGs.push_back(fg); + OFCHECK(seg->addFrame(data, forget, perFrameFGs).good()); + delete[] data; + } + delete fg; +} + +static void addDimensions(DcmSegmentation* seg) +{ + if (!seg) + return; + IODMultiframeDimensionModule& dims = seg->getDimensions(); + OFCHECK(dims.addDimensionIndex( + DCM_StackID, "2.25.30855560781715986879861690673941231222", DCM_FrameContentSequence, "STACK_DIM") + .good()); + OFCHECK(dims.addDimensionIndex(DCM_InStackPositionNumber, + "2.25.30855560781715986879861690673941231222", + DCM_FrameContentSequence, + "STACK_DIM") + .good()); + OFunique_ptr org( + new IODMultiframeDimensionModule::DimensionOrganizationItem); + if (org) + { + org->setDimensionOrganizationUID("2.25.30855560781715986879861690673941231222"); + dims.getDimensionOrganizationSequence().push_back(org.release()); + } +} + +static OFString write(DcmSegmentation* seg, DcmDataset& ds) +{ + OFCondition result = seg->writeDataset(ds); + OFCHECK(result.good()); + + return ""; +} diff --git a/dcmseg/tests/troundtrip.cc b/dcmseg/tests/troundtrip.cc index d9df8ceb..97c96e6c 100644 --- a/dcmseg/tests/troundtrip.cc +++ b/dcmseg/tests/troundtrip.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019-2024, OFFIS e.V. + * Copyright (C) 2019-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -36,6 +36,7 @@ #include "dcmtk/ofstd/ofmem.h" #include "dcmtk/ofstd/oftempf.h" #include "dcmtk/ofstd/oftest.h" +//#include static const Uint8 NUM_ROWS = 10; static const Uint8 NUM_COLS = 10; @@ -150,8 +151,10 @@ static void setGenericValues(DcmSegmentation* seg) OFCHECK(seg->getSOPCommon().setSOPInstanceUID("1.2.276.0.7230010.3.1.4.8323329.14863.1565940357.864813").good()); OFCHECK(seg->getGeneralImage().setContentDate("20190927").good()); OFCHECK(seg->getGeneralImage().setContentTime("153857").good()); + OFCHECK(seg->getGeneralImage().setContentTime("153857").good()); } + static void addSharedFGs(DcmSegmentation* seg) { if (!seg) @@ -178,9 +181,9 @@ static void addFrames(DcmSegmentation* seg) if (!seg) return; - FGSegmentation* fg_seg = new FGSegmentation(); + // Segmentation FG is created automatically FGFrameContent* fg = new FGFrameContent(); - OFCHECK(fg && fg_seg); + OFCHECK(fg); fg->setStackID("1"); if (fg) { @@ -209,16 +212,13 @@ static void addFrames(DcmSegmentation* seg) { data[i] = i; } - OFCHECK(fg_seg->setReferencedSegmentNumber(frameNo).good()); OFVector perFrameFGs; perFrameFGs.push_back(fg); - perFrameFGs.push_back(fg_seg); - OFCHECK(seg->addFrame(data, frameNo, perFrameFGs).good()); + OFCHECK(seg->addFrame(data, frameNo, perFrameFGs).good()); delete[] data; } } delete fg; - delete fg_seg; } static void addDimensions(DcmSegmentation* seg) @@ -354,13 +354,13 @@ static void checkConcatenationInstance(size_t numInstance, DcmSegmentation* srcI OFCHECK(fg != NULL); OFCHECK(perFrame == OFFalse); - const DcmIODTypes::Frame* frame = concat->getFrame(0); + const DcmIODTypes::Frame* frame = OFstatic_cast(const DcmIODTypes::Frame*, concat->getFrame(0)); OFCHECK(frame != OFnullptr); - OFCHECK(frame->pixData != OFnullptr); - OFCHECK(OFstatic_cast(Uint8, frame->length) == NUM_PIXELS_PER_FRAME); - for (size_t pix = 0; pix < frame->length; pix++) + OFCHECK(frame->m_pixData != OFnullptr); + OFCHECK(OFstatic_cast(Uint8, frame->getLengthInBytes()) == NUM_PIXELS_PER_FRAME); + for (size_t pix = 0; pix < frame->getLengthInBytes(); pix++) { - OFCHECK(frame->pixData[pix] == pix); + OFCHECK(frame->m_pixData[pix] == pix); } delete concat; } @@ -719,6 +719,7 @@ static void prepareExpectedDump() EXPECTED_DUMP += "(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n"; EXPECTED_DUMP += "(0062,000e) US 255 # 2, 1 MaximumFractionalValue\n"; EXPECTED_DUMP += "(0062,0010) CS [OCCUPANCY] # 10, 1 SegmentationFractionalType\n"; + EXPECTED_DUMP += "(0062,0013) CS [UNDEFINED] # 10, 1 SegmentsOverlap\n"; EXPECTED_DUMP += "(0070,0080) CS [LABEL] # 6, 1 ContentLabel\n"; EXPECTED_DUMP += "(0070,0081) LO [DESCRIPTION] # 12, 1 ContentDescription\n"; EXPECTED_DUMP += "(0070,0084) PN [Doe^John] # 8, 1 ContentCreatorName\n"; diff --git a/dcmseg/tests/tutils.cc b/dcmseg/tests/tutils.cc index 37405859..03217aac 100644 --- a/dcmseg/tests/tutils.cc +++ b/dcmseg/tests/tutils.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2024, OFFIS e.V. + * Copyright (C) 2015-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -21,10 +21,12 @@ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ -#include "dcmtk/dcmiod/iodtypes.h" #include "dcmtk/dcmseg/segutils.h" +#include "dcmtk/dcmseg/segtypes.h" #include "dcmtk/ofstd/oftest.h" #include "dcmtk/ofstd/ofstd.h" +#include "dcmtk/ofstd/oftime.h" // For debugByte2Bin +#include #define bufLen 4 @@ -32,29 +34,28 @@ // Check whether packing of sparse frames into binary packed frames works correctly OFTEST(dcmseg_packBinaryFrame) { - // Check whether the following statically defined frames are packed correctly Uint8 sparseFrame1[8] = {1, 1, 1, 1, 0, 0, 0, 0}; - DcmIODTypes::Frame* packed = DcmSegUtils::packBinaryFrame(sparseFrame1, 4, 2); + DcmIODTypes::Frame* packed = DcmSegUtils::packBinaryFrame(sparseFrame1, 4, 2); OFCHECK(packed != NULL); - OFCHECK(packed->length == 1); - OFCHECK_MSG(packed->pixData[0] == 0b00001111, OFString("Expected 0b00001111, got ") + DcmSegUtils::debugByte2Bin(packed->pixData[0])); + OFCHECK(packed->getLengthInBytes() == 1); + OFCHECK_MSG(OFstatic_cast(Uint8*, packed->getPixelData())[0] == 0b00001111, OFString("Expected 0b00001111, got ") + DcmSegUtils::debugByte2Bin( OFstatic_cast(Uint8*, packed->getPixelData())[0])); delete packed; Uint8 sparseFrame2[8] = {1, 0, 1, 0, 1, 0, 1, 0}; packed = DcmSegUtils::packBinaryFrame(sparseFrame2, 4, 2); OFCHECK(packed != NULL); - OFCHECK(packed->length == 1); - OFCHECK_MSG(packed->pixData[0] == 0b01010101, OFString("Expected 0b01010101, got ") + DcmSegUtils::debugByte2Bin(packed->pixData[0])); + OFCHECK(packed->getLengthInBytes() == 1); + OFCHECK_MSG(OFstatic_cast(Uint8*, packed->getPixelData())[0] == 0b01010101, OFString("Expected 0b01010101, got ") + DcmSegUtils::debugByte2Bin( OFstatic_cast(Uint8*, packed->getPixelData())[0])); delete packed; // Now try the that is larger than a byte and not a multiple of 8, with every third pixel set to 1 Uint8 sparseFrame3[15] = {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1}; packed = DcmSegUtils::packBinaryFrame(sparseFrame3, 5, 3); OFCHECK(packed != NULL); - OFCHECK(packed->length == 2); - OFCHECK_MSG(packed->pixData[0] == 0b00100100, OFString("Expected 0b00100100, got ") + DcmSegUtils::debugByte2Bin(packed->pixData[0])); - OFCHECK_MSG(packed->pixData[1] == 0b01001001, OFString("Expected 0b01001001, got ") + DcmSegUtils::debugByte2Bin(packed->pixData[1])); + OFCHECK(packed->getLengthInBytes() == 2); + OFCHECK_MSG(OFstatic_cast(Uint8*, packed->getPixelData())[0] == 0b00100100, OFString("Expected 0b00100100, got ") + DcmSegUtils::debugByte2Bin( OFstatic_cast(Uint8*, packed->getPixelData())[0])); + OFCHECK_MSG(OFstatic_cast(Uint8*, packed->getPixelData())[1] == 0b01001001, OFString("Expected 0b01001001, got ") + DcmSegUtils::debugByte2Bin( OFstatic_cast(Uint8*, packed->getPixelData())[1])); delete packed; // Now the same but with every 5th pixel set to 1, and rows=7 and cols=5 @@ -66,21 +67,23 @@ OFTEST(dcmseg_packBinaryFrame) } packed = DcmSegUtils::packBinaryFrame(sparseFrame4, 7, 5); OFCHECK(packed != NULL); - OFCHECK(packed->length == 5); - OFCHECK_MSG(packed->pixData[0] == 0b00100001, OFString("Expected 0b00100001, got ") + DcmSegUtils::debugByte2Bin(packed->pixData[0])); - OFCHECK_MSG(packed->pixData[1] == 0b10000100, OFString("Expected 0b10000100, got ") + DcmSegUtils::debugByte2Bin(packed->pixData[1])); - OFCHECK_MSG(packed->pixData[2] == 0b00010000, OFString("Expected 0b00010000, got ") + DcmSegUtils::debugByte2Bin(packed->pixData[2])); - OFCHECK_MSG(packed->pixData[3] == 0b01000010, OFString("Expected 0b01000010, got ") + DcmSegUtils::debugByte2Bin(packed->pixData[3])); - OFCHECK_MSG(packed->pixData[4] == 0b00000000, OFString("Expected 0b00000000, got ") + DcmSegUtils::debugByte2Bin(packed->pixData[4])); + OFCHECK(packed->getLengthInBytes() == 5); + OFCHECK_MSG(OFstatic_cast(Uint8*, packed->getPixelData())[0] == 0b00100001, OFString("Expected 0b00100001, got ") + DcmSegUtils::debugByte2Bin( OFstatic_cast(Uint8*, packed->getPixelData())[0])); + OFCHECK_MSG(OFstatic_cast(Uint8*, packed->getPixelData())[1] == 0b10000100, OFString("Expected 0b10000100, got ") + DcmSegUtils::debugByte2Bin( OFstatic_cast(Uint8*, packed->getPixelData())[1])); + OFCHECK_MSG(OFstatic_cast(Uint8*, packed->getPixelData())[2] == 0b00010000, OFString("Expected 0b00010000, got ") + DcmSegUtils::debugByte2Bin( OFstatic_cast(Uint8*, packed->getPixelData())[2])); + OFCHECK_MSG(OFstatic_cast(Uint8*, packed->getPixelData())[3] == 0b01000010, OFString("Expected 0b01000010, got ") + DcmSegUtils::debugByte2Bin( OFstatic_cast(Uint8*, packed->getPixelData())[3])); + OFCHECK_MSG(OFstatic_cast(Uint8*, packed->getPixelData())[4] == 0b00000000, OFString("Expected 0b00000000, got ") + DcmSegUtils::debugByte2Bin( OFstatic_cast(Uint8*, packed->getPixelData())[4])); + delete packed; // In 1000 iterations create sparse frames and pack them. Check whether the // packed frame is correct. This is not bullet proof but we use the same addressing - // as in the packing/unpacking code to make sure we address the right bit. + // as in the packing/unpacking code to make sure we address the correct bit. // Use a random number of cols and rows (each between 1 and 100). // If the packed frame is not correct, the test fails. - unsigned int now = OFstatic_cast(unsigned int, time(NULL)); + OFTime tm; for (unsigned int i = 0; i < 1000; i++) { + unsigned int now = OFstatic_cast(unsigned int, time(NULL)); Uint16 cols = OFrand_r(now) % 100 + 1; Uint16 rows = OFrand_r(now) % 100 + 1; Uint16 pixelCount = cols * rows; @@ -91,11 +94,12 @@ OFTEST(dcmseg_packBinaryFrame) // Create a random sparse frame for (unsigned int j = 0; j < pixelCount; j++) { - sparseFrame[j] = OFrand_r(now) % 2; + tm.setCurrentTime(); + unsigned int micro = tm.getMicroSecond(); + sparseFrame[j] = OFrand_r(micro) % 2; } - // Pack the frame - DcmIODTypes::Frame* packedFrame = DcmSegUtils::packBinaryFrame(sparseFrame, rows, cols); + DcmIODTypes::Frame* packedFrame = DcmSegUtils::packBinaryFrame(sparseFrame, rows, cols); OFCHECK(packedFrame != NULL); // Check the result @@ -104,12 +108,15 @@ OFTEST(dcmseg_packBinaryFrame) Uint32 byteIndex = j / 8; Uint32 bitIndex = j % 8; Uint8 mask = 1 << bitIndex; - if ((sparseFrame[j] != 0) != ((packedFrame->pixData[byteIndex] & mask) != 0)) + Uint8 currentByte = OFstatic_cast(Uint8*, packedFrame->getPixelData())[byteIndex]; + // Check whether the bit at position j is set correctly + if ((sparseFrame[j] == 0) != ((currentByte & mask) == 0)) { OFCHECK_FAIL("Failed for row " << j / cols << " and column " << j % cols); } } + // Clean up delete[] sparseFrame; delete packedFrame; } @@ -130,8 +137,8 @@ OFTEST(dcmseg_packAndUnpackBinaryFrame) Uint8* sparseFrame = new Uint8[pixelCount]; OFCHECK(sparseFrame != NULL); - DcmIODTypes::Frame* packed; - DcmIODTypes::Frame* unpacked; + DcmIODTypes::FrameBase* packed; + DcmIODTypes::FrameBase* unpacked; // Create a random sparse frame for (unsigned int j = 0; j < pixelCount; j++) @@ -142,11 +149,11 @@ OFTEST(dcmseg_packAndUnpackBinaryFrame) // Pack and unpack the frame packed = DcmSegUtils::packBinaryFrame(sparseFrame, rows, cols); OFCHECK(packed != NULL); - unpacked = DcmSegUtils::unpackBinaryFrame(packed, rows, cols); + unpacked = DcmSegUtils::unpackBinaryFrame(OFstatic_cast(DcmIODTypes::Frame*, packed), rows, cols); OFCHECK(unpacked != NULL); // Compare the result - OFCHECK(memcmp(sparseFrame, unpacked->pixData, pixelCount) == 0); + OFCHECK(memcmp(sparseFrame, unpacked->getPixelData(), pixelCount) == 0); delete[] sparseFrame; delete packed; @@ -162,20 +169,20 @@ OFTEST(dcmseg_packAndUnpackBinaryFrame) Uint8* sparseFrame = new Uint8[pixelCount]; OFCHECK(sparseFrame != NULL); memset(sparseFrame, 0, pixelCount); - DcmIODTypes::Frame* packed = DcmSegUtils::packBinaryFrame(sparseFrame, rows, cols); + DcmIODTypes::FrameBase* packed = DcmSegUtils::packBinaryFrame(sparseFrame, rows, cols); OFCHECK(packed != NULL); - DcmIODTypes::Frame* unpacked = DcmSegUtils::unpackBinaryFrame(packed, rows, cols); + DcmIODTypes::FrameBase* unpacked = DcmSegUtils::unpackBinaryFrame(OFstatic_cast(DcmIODTypes::Frame*, packed), rows, cols); OFCHECK(unpacked != NULL); - OFCHECK(memcmp(sparseFrame, unpacked->pixData, pixelCount) == 0); + OFCHECK(memcmp(sparseFrame, unpacked->getPixelData(), pixelCount) == 0); delete packed; delete unpacked; memset(sparseFrame, 1, pixelCount); packed = DcmSegUtils::packBinaryFrame(sparseFrame, rows, cols); OFCHECK(packed != NULL); - unpacked = DcmSegUtils::unpackBinaryFrame(packed, rows, cols); + unpacked = DcmSegUtils::unpackBinaryFrame(OFstatic_cast(DcmIODTypes::Frame*, packed), rows, cols); OFCHECK(unpacked != NULL); - OFCHECK(memcmp(sparseFrame, unpacked->pixData, pixelCount) == 0); + OFCHECK(memcmp(sparseFrame, unpacked->getPixelData(), pixelCount) == 0); delete packed; delete unpacked; @@ -190,11 +197,11 @@ OFTEST(dcmseg_concatBinaryFrames) const int cols = 4; Uint8 sparseFrame1[rows * cols] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; Uint8 sparseFrame2[rows * cols] = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - DcmIODTypes::Frame* packed1 = DcmSegUtils::packBinaryFrame(sparseFrame1, rows, cols); - DcmIODTypes::Frame* packed2 = DcmSegUtils::packBinaryFrame(sparseFrame2, rows, cols); + DcmIODTypes::FrameBase* packed1 = DcmSegUtils::packBinaryFrame(sparseFrame1, rows, cols); + DcmIODTypes::FrameBase* packed2 = DcmSegUtils::packBinaryFrame(sparseFrame2, rows, cols); OFCHECK(packed1 != NULL); OFCHECK(packed2 != NULL); - OFVector inputFrames; + OFVector inputFrames; inputFrames.push_back(packed1); inputFrames.push_back(packed2); // Now concatenate the two frames into a single bit array @@ -206,6 +213,10 @@ OFTEST(dcmseg_concatBinaryFrames) OFCHECK_MSG(pixData[0] == 0b11111111, OFString("Expected 0b11111111, got ") + DcmSegUtils::debugByte2Bin(pixData[0])); OFCHECK_MSG(pixData[1] == 0b11101111, OFString("Expected 0b11101111, got ") + DcmSegUtils::debugByte2Bin(pixData[1])); OFCHECK_MSG(pixData[2] == 0b11111111, OFString("Expected 0b11111111, got ") + DcmSegUtils::debugByte2Bin(pixData[2])); + // avoid memory leaks + delete packed1; + delete packed2; + delete[] pixData; } // Test DcmSegUtils::debugByte2Bin() diff --git a/dcmsign/docs/dcmsign.man b/dcmsign/docs/dcmsign.man index 998ecb38..4ca506c2 100644 --- a/dcmsign/docs/dcmsign.man +++ b/dcmsign/docs/dcmsign.man @@ -582,6 +582,6 @@ It is an error if no data dictionary can be loaded. \section dcmsign_copyright COPYRIGHT -Copyright (C) 2000-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2000-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmsr/apps/Makefile.dep b/dcmsr/apps/Makefile.dep index 242f5128..c80e718c 100644 --- a/dcmsr/apps/Makefile.dep +++ b/dcmsr/apps/Makefile.dep @@ -86,8 +86,9 @@ dsr2html.o: dsr2html.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmsr/dsrwavch.h ../include/dcmtk/dcmsr/dsrrtpl.h \ ../include/dcmtk/dcmsr/dsrctpl.h ../include/dcmtk/dcmsr/dsrsoprf.h \ ../include/dcmtk/dcmsr/dsrrefin.h ../include/dcmtk/dcmsr/dsrcsidl.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrda.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ @@ -116,7 +117,6 @@ dsr2html.o: dsr2html.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpixseq.h \ ../../dcmdata/include/dcmtk/dcmdata/dcofsetl.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrae.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrdt.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrur.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ @@ -231,8 +231,9 @@ dsr2xml.o: dsr2xml.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmsr/dsrwavch.h ../include/dcmtk/dcmsr/dsrrtpl.h \ ../include/dcmtk/dcmsr/dsrctpl.h ../include/dcmtk/dcmsr/dsrsoprf.h \ ../include/dcmtk/dcmsr/dsrrefin.h ../include/dcmtk/dcmsr/dsrcsidl.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrda.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ @@ -261,7 +262,6 @@ dsr2xml.o: dsr2xml.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpixseq.h \ ../../dcmdata/include/dcmtk/dcmdata/dcofsetl.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrae.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrdt.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrur.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ @@ -376,8 +376,9 @@ dsrdump.o: dsrdump.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmsr/dsrwavch.h ../include/dcmtk/dcmsr/dsrrtpl.h \ ../include/dcmtk/dcmsr/dsrctpl.h ../include/dcmtk/dcmsr/dsrsoprf.h \ ../include/dcmtk/dcmsr/dsrrefin.h ../include/dcmtk/dcmsr/dsrcsidl.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrda.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ @@ -406,7 +407,6 @@ dsrdump.o: dsrdump.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpixseq.h \ ../../dcmdata/include/dcmtk/dcmdata/dcofsetl.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrae.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrdt.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrur.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ @@ -521,8 +521,9 @@ xml2dsr.o: xml2dsr.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmsr/dsrwavch.h ../include/dcmtk/dcmsr/dsrrtpl.h \ ../include/dcmtk/dcmsr/dsrctpl.h ../include/dcmtk/dcmsr/dsrsoprf.h \ ../include/dcmtk/dcmsr/dsrrefin.h ../include/dcmtk/dcmsr/dsrcsidl.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrda.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ @@ -551,7 +552,6 @@ xml2dsr.o: xml2dsr.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpixseq.h \ ../../dcmdata/include/dcmtk/dcmdata/dcofsetl.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrae.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrdt.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrur.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ diff --git a/dcmsr/apps/dsr2html.cc b/dcmsr/apps/dsr2html.cc index 469611ed..a1b76541 100644 --- a/dcmsr/apps/dsr2html.cc +++ b/dcmsr/apps/dsr2html.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -207,7 +207,9 @@ int main(int argc, char *argv[]) E_FileReadMode opt_readMode = ERM_autoDetect; E_TransferSyntax opt_ixfer = EXS_Unknown; OFBool opt_checkAllStrings = OFFalse; +#ifdef DCMTK_ENABLE_CHARSET_CONVERSION OFBool opt_convertToUTF8 = OFFalse; +#endif OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Render DICOM SR file and data set to HTML/XHTML", rcsid); OFCommandLine cmd; diff --git a/dcmsr/apps/dsrdump.cc b/dcmsr/apps/dsrdump.cc index e2036820..590b3c21 100644 --- a/dcmsr/apps/dsrdump.cc +++ b/dcmsr/apps/dsrdump.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2023, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by diff --git a/dcmsr/apps/xml2dsr.cc b/dcmsr/apps/xml2dsr.cc index 054e58d4..c4309b55 100644 --- a/dcmsr/apps/xml2dsr.cc +++ b/dcmsr/apps/xml2dsr.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2003-2024, OFFIS e.V. + * Copyright (C) 2003-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -30,6 +30,7 @@ #include "dcmtk/ofstd/ofstream.h" #include "dcmtk/ofstd/ofconapp.h" +#include "dcmtk/ofstd/ofdiag.h" #ifdef WITH_ZLIB #include /* for zlibVersion() */ @@ -56,8 +57,12 @@ static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" #define LIBXML_ATTR_FORMAT(fmt,args) #endif +// MacOS 15.5 defines some Clang specific pragmas in libxml header files. +// Suppress warnings caused by these pragmas when compiling with GCC. +#include DCMTK_DIAGNOSTIC_PUSH +#include DCMTK_DIAGNOSTIC_IGNORE_CLANG_PRAGMAS_ON_GCC #include - +#include DCMTK_DIAGNOSTIC_POP #define SHORTCOL 3 #define LONGCOL 21 diff --git a/dcmsr/data/dsr2xml.xsd b/dcmsr/data/dsr2xml.xsd index 7230eb2e..b24f28b1 100644 --- a/dcmsr/data/dsr2xml.xsd +++ b/dcmsr/data/dsr2xml.xsd @@ -10,7 +10,7 @@ XML Schema for DCMTK tools dsr2xml and xml2dsr. - Copyright (C) 2003-2024, OFFIS e.V. and J. Riesmeier + Copyright (C) 2003-2025, OFFIS e.V. and J. Riesmeier All rights reserved. See COPYRIGHT file for details. @@ -91,7 +91,8 @@ - + + @@ -980,6 +981,12 @@ + + + + + + diff --git a/dcmsr/docs/dsr2html.man b/dcmsr/docs/dsr2html.man index fe3bdd48..adef1e25 100644 --- a/dcmsr/docs/dsr2html.man +++ b/dcmsr/docs/dsr2html.man @@ -257,8 +257,12 @@ PatientRadiationDoseSRStorage 1.2.840.10008.5.1.4.1.1.88.73 PlannedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.74 PerformedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.75 WaveformAnnotationSRStorage 1.2.840.10008.5.1.4.1.1.88.77 + +RenditionSelectionDocumentRealTimeCommunication 1.2.840.10008.10.4 (*) \endverbatim +(*) This is not a Storage SOP Class, but used for Real-Time Communication. + \subsection dsr2html_character_encoding Character Encoding The HTML/XHTML encoding is determined automatically from the DICOM attribute @@ -296,6 +300,14 @@ hebrew. Option \e --convert-to-utf8 can be used to convert the DICOM file or data set to UTF-8 encoding prior to the rendering to HTML/XHTML format. +\subsection dsr2html_security Security + +Please note that using one of the options \e --css-reference, \e --css-file or +\e --hyperlink-url-prefix can lead to security issues, as an attacker could +misuse them to potentially inject dangerous content into the HTML/XHTML output. +The values passed to these options are not checked, neither the URL and prefix +nor the content of the specified CSS file. + \section dsr2html_logging LOGGING The level of logging output of the various command line tools and underlying @@ -366,7 +378,7 @@ replace any built-in tables. \section dsr2html_files FILES \/report.css - Sample Cascading Stylesheet file for HTML -\/reportx.css - Sample Cascading Stylesheet file for XHTML +\n\/reportx.css - Sample Cascading Stylesheet file for XHTML \section dsr2html_see_also SEE ALSO @@ -374,6 +386,6 @@ replace any built-in tables. \section dsr2html_copyright COPYRIGHT -Copyright (C) 2000-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2000-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmsr/docs/dsr2xml.man b/dcmsr/docs/dsr2xml.man index 35a65e7a..a24cb426 100644 --- a/dcmsr/docs/dsr2xml.man +++ b/dcmsr/docs/dsr2xml.man @@ -220,8 +220,12 @@ PatientRadiationDoseSRStorage 1.2.840.10008.5.1.4.1.1.88.73 PlannedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.74 PerformedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.75 WaveformAnnotationSRStorage 1.2.840.10008.5.1.4.1.1.88.77 + +RenditionSelectionDocumentRealTimeCommunication 1.2.840.10008.10.4 (*) \endverbatim +(*) This is not a Storage SOP Class, but used for Real-Time Communication. + Please note that currently only mandatory and some optional attributes are supported. @@ -359,6 +363,6 @@ replace any built-in tables. \section dsr2xml_copyright COPYRIGHT -Copyright (C) 2000-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2000-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmsr/docs/dsrdump.man b/dcmsr/docs/dsrdump.man index a15e3464..06734395 100644 --- a/dcmsr/docs/dsrdump.man +++ b/dcmsr/docs/dsrdump.man @@ -226,8 +226,12 @@ PatientRadiationDoseSRStorage 1.2.840.10008.5.1.4.1.1.88.73 PlannedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.74 PerformedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.75 WaveformAnnotationSRStorage 1.2.840.10008.5.1.4.1.1.88.77 + +RenditionSelectionDocumentRealTimeCommunication 1.2.840.10008.10.4 (*) \endverbatim +(*) This is not a Storage SOP Class, but used for Real-Time Communication. + \section dsrdump_logging LOGGING The level of logging output of the various command line tools and underlying @@ -301,6 +305,6 @@ replace any built-in tables. \section dsrdump_copyright COPYRIGHT -Copyright (C) 2000-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2000-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmsr/docs/xml2dsr.man b/dcmsr/docs/xml2dsr.man index 60dd451b..6a0cea28 100644 --- a/dcmsr/docs/xml2dsr.man +++ b/dcmsr/docs/xml2dsr.man @@ -194,8 +194,12 @@ PatientRadiationDoseSRStorage 1.2.840.10008.5.1.4.1.1.88.73 PlannedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.74 PerformedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.75 WaveformAnnotationSRStorage 1.2.840.10008.5.1.4.1.1.88.77 + +RenditionSelectionDocumentRealTimeCommunication 1.2.840.10008.10.4 (*) \endverbatim +(*) This is not a Storage SOP Class, but used for Real-Time Communication. + Please note that currently only mandatory and some optional attributes are supported. @@ -312,6 +316,6 @@ It is an error if no data dictionary can be loaded. \section xml2dsr_copyright COPYRIGHT -Copyright (C) 2003-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 2003-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid100.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid100.h index 560b3493..f4bc85ca 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid100.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid100.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID100_QuantitativeDiagnosticImagingProcedure * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:09 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:43 by J. Riesmeier * */ @@ -28,7 +28,7 @@ /** Implementation of DCMR Context Group: * CID 100 - Quantitative Diagnostic Imaging Procedure. - * (type: extensible, version: 20230630) + * (type: extensible, version: 20250122) */ class DCMTK_CMR_EXPORT CID100_QuantitativeDiagnosticImagingProcedure : public DSRContextGroup @@ -75,9 +75,7 @@ class DCMTK_CMR_EXPORT CID100_QuantitativeDiagnosticImagingProcedure /// (39142-5,LN,"CT perfusion head with contrast IV") CTPerfusionHeadWithContrastIV, /// (39632-5,LN,"SPECT brain") - SPECTBrain, - /// (RPID5427,RADLEX,"NM head perfusion brain PET-CT AV-45") - NMHeadPerfusionBrainPET_CT_AV45 + SPECTBrain }; /** (default) constructor diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid10013.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid10013.h index 928cafa4..5b268dd5 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid10013.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid10013.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID10013_CTAcquisitionType * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:25 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:57 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid10033.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid10033.h index 2e87fa4d..b133a73c 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid10033.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid10033.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID10033_CTReconstructionAlgorithm * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:26 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:58 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid11.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid11.h index d222daef..b5599edb 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid11.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid11.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID11_AdministrationRoute * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:06 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:40 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid218.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid218.h index 65bf3196..63b92a61 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid218.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid218.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID218_QuantitativeImageFeature * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:10 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:44 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid244.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid244.h index cb38c107..e02bb87e 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid244.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid244.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID244_Laterality * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:11 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:45 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid247.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid247.h index de4afe1d..aa912612 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid247.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid247.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID247_LateralityLeftRightOnly * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:12 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:45 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid29.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid29.h index a9666a8d..a51f95f5 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid29.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid29.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID29_AcquisitionModality * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:07 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:41 by J. Riesmeier * */ @@ -72,8 +72,8 @@ class DCMTK_CMR_EXPORT CID29_AcquisitionModality IntravascularUltrasound, /// (KER,DCM,"Keratometry") Keratometry, - /// (LS,DCM,"Laser Scan") - LaserScan, + /// (LS,DCM,"Laser surface scan") + LaserSurfaceScan, /// (LEN,DCM,"Lensometry") Lensometry, /// (MR,DCM,"Magnetic Resonance") diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid4020.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid4020.h index 75f9b163..ece2202f 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid4020.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid4020.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID4020_PETRadionuclide * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:13 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:46 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid4021.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid4021.h index c63f4b57..257f010c 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid4021.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid4021.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID4021_PETRadiopharmaceutical * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:14 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:47 by J. Riesmeier * */ @@ -28,7 +28,7 @@ /** Implementation of DCMR Context Group: * CID 4021 - PET Radiopharmaceutical. - * (type: extensible, version: 20221201) + * (type: extensible, version: 20251111) */ class DCMTK_CMR_EXPORT CID4021_PETRadiopharmaceutical : public DSRContextGroup @@ -44,6 +44,10 @@ class DCMTK_CMR_EXPORT CID4021_PETRadiopharmaceutical _28H1_89Zr, /// (126713,DCM,"2FA F^18^") _2FA_F18, + /// (C90936,NCIt,"2-Thymidine C^11^") + _2Thymidine_C11, + /// (771875003,SCT,"3-N-Methylspiperone C^11^") + _3NMethylspiperone_C11, /// (126751,DCM,"7D12 ^89^Zr") _7D12_89Zr, /// (126750,DCM,"7E11 ^89^Zr") @@ -96,10 +100,10 @@ class DCMTK_CMR_EXPORT CID4021_PETRadiopharmaceutical CMAbU36_89Zr, /// (126515,DCM,"cU36 ^89^Zr") CU36_89Zr, + /// (C412822,MSH,"DASB C^11^") + DASB_C11, /// (C96234,NCIt,"DCFBC F^18^") DCFBC_F18, - /// (C116352,NCIt,"Piflufolastat F^18^") - Piflufolastat_F18, /// (126762,DCM,"Df-[FK](2) ^89^Zr") DfFK2_89Zr, /// (126763,DCM,"Df-[FK](2)-3PEG(4) ^89^Zr") @@ -112,6 +116,8 @@ class DCMTK_CMR_EXPORT CID4021_PETRadiopharmaceutical DfFKPEG3_89Zr, /// (126747,DCM,"DN30 ^89^Zr") DN30_89Zr, + /// (724025002,SCT,"Dotatate Ga^68^") + Dotatate_Ga68, /// (126765,DCM,"DPA-713 ^11^C") DPA713_11C, /// (126766,DCM,"DPA-714 ^18^F") @@ -148,6 +154,8 @@ class DCMTK_CMR_EXPORT CID4021_PETRadiopharmaceutical Flumazenil_F18, /// (424708001,SCT,"Fluorethyltyrosin F^18^") Fluorethyltyrosin_F18, + /// (C62520,NCIt,"Fluoroazomycin arabinoside F^18^") + FluoroazomycinArabinoside_F18, /// (423546004,SCT,"Fluorobenzothiazole F^18^") Fluorobenzothiazole_F18, /// (456992002,SCT,"Fluorocholine F^18^") @@ -166,6 +174,8 @@ class DCMTK_CMR_EXPORT CID4021_PETRadiopharmaceutical Fluoromisonidazole_F18, /// (C2934038,UMLS,"Fluoropropyl-dihydrotetrabenazine F^18^") FluoropropylDihydrotetrabenazine_F18, + /// (764937002,SCT,"Fluorothymidine F^18^") + Fluorothymidine_F18, /// (126707,DCM,"Fluorotriopride F^18^") Fluorotriopride_F18, /// (425236000,SCT,"Fluorouracil F^18^") @@ -182,6 +192,8 @@ class DCMTK_CMR_EXPORT CID4021_PETRadiopharmaceutical Germanium_Ge68, /// (126724,DCM,"Glembatumumab vedotin ^89^Zr") GlembatumumabVedotin_89Zr, + /// (126521,DCM,"Glucose C^11^") + Glucose_C11, /// (129509006,SCT,"Glutamate N^13^") Glutamate_N13, /// (126709,DCM,"Glutamine C^11^") @@ -230,6 +242,12 @@ class DCMTK_CMR_EXPORT CID4021_PETRadiopharmaceutical Panitumumab_89Zr, /// (126728,DCM,"Pegdinetanib ^89^Zr") Pegdinetanib_89Zr, + /// (C148167,NCIt,"Pembrolizumab ^89^Zr") + Pembrolizumab_89Zr, + /// (C5433257,UMLS,"PI-2620 F^18^") + PI2620_F18, + /// (C116352,NCIt,"Piflufolastat F^18^") + Piflufolastat_F18, /// (126725,DCM,"Pinatuzumab vedotin ^89^Zr") PinatuzumabVedotin_89Zr, /// (126500,DCM,"Pittsburgh compound B C^11^") @@ -286,14 +304,10 @@ class DCMTK_CMR_EXPORT CID4021_PETRadiopharmaceutical THK5317_F18, /// (C4279748,UMLS,"THK5351 F^18^") THK5351_F18, - /// (129502002,SCT,"Thymidine F^18^") - Thymidine_F18, /// (126512,DCM,"Trastuzumab ^89^Zr") Trastuzumab_89Zr, /// (126749,DCM,"TRC105 ^89^Zr") TRC105_89Zr, - /// (724025002,SCT,"Dotatate Ga^68^") - Dotatate_Ga68, /// (126739,DCM,"Ublituximab ^89^Zr") Ublituximab_89Zr, /// (C4506788,UMLS,"UCB-J C^11^") diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid4031.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid4031.h index 6ae882a7..513aca4b 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid4031.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid4031.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID4031_CommonAnatomicRegion * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:15 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:48 by J. Riesmeier * */ @@ -28,7 +28,7 @@ /** Implementation of DCMR Context Group: * CID 4031 - Common Anatomic Region. - * (type: extensible, version: 20221224) + * (type: extensible, version: 20250709) */ class DCMTK_CMR_EXPORT CID4031_CommonAnatomicRegion : public DSRContextGroup @@ -68,16 +68,18 @@ class DCMTK_CMR_EXPORT CID4031_CommonAnatomicRegion Bronchus, /// (80144004,SCT,"Calcaneus") Calcaneus, + /// (113257007,SCT,"Cardiovascular system") + CardiovascularSystem, /// (122494005,SCT,"Cervical spine") CervicalSpine, /// (1217257000,SCT,"Cervico-thoracic spine") CervicoThoracicSpine, /// (816094009,SCT,"Chest") Chest, - /// (416550000,SCT,"Chest and Abdomen") - ChestAndAbdomen, /// (416775004,SCT,"Chest, Abdomen and Pelvis") ChestAbdomenAndPelvis, + /// (416550000,SCT,"Chest and Abdomen") + ChestAndAbdomen, /// (51299004,SCT,"Clavicle") Clavicle, /// (64688005,SCT,"Coccyx") @@ -152,6 +154,8 @@ class DCMTK_CMR_EXPORT CID4031_CommonAnatomicRegion LowerLeg, /// (61685007,SCT,"Lower limb") LowerLimb, + /// (63337009,SCT,"Lower trunk") + LowerTrunk, /// (122496007,SCT,"Lumbar spine") LumbarSpine, /// (1217253001,SCT,"Lumbo-sacral spine") @@ -172,12 +176,12 @@ class DCMTK_CMR_EXPORT CID4031_CommonAnatomicRegion NasalBone, /// (45048000,SCT,"Neck") Neck, - /// (417437006,SCT,"Neck and Chest") - NeckAndChest, - /// (416152001,SCT,"Neck, Chest and Abdomen") - NeckChestAndAbdomen, /// (416319003,SCT,"Neck, Chest, Abdomen and Pelvis") NeckChestAbdomenAndPelvis, + /// (416152001,SCT,"Neck, Chest and Abdomen") + NeckChestAndAbdomen, + /// (417437006,SCT,"Neck and Chest") + NeckAndChest, /// (55024004,SCT,"Optic canal") OpticCanal, /// (363654007,SCT,"Orbital structure") @@ -226,6 +230,8 @@ class DCMTK_CMR_EXPORT CID4031_CommonAnatomicRegion SmallIntestine, /// (421060004,SCT,"Spine") Spine, + /// (737561001,SCT,"Spine and/or cord") + SpineAndPerOrCord, /// (7844006,SCT,"Sternoclavicular joint") SternoclavicularJoint, /// (56873002,SCT,"Sternum") @@ -250,10 +256,14 @@ class DCMTK_CMR_EXPORT CID4031_CommonAnatomicRegion Toe, /// (44567001,SCT,"Trachea") Trachea, + /// (22943007,SCT,"Trunk") + Trunk, /// (40983000,SCT,"Upper arm") UpperArm, /// (53120007,SCT,"Upper limb") UpperLimb, + /// (67734004,SCT,"Upper trunk") + UpperTrunk, /// (431491007,SCT,"Upper urinary tract") UpperUrinaryTract, /// (87953007,SCT,"Ureter") diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid42.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid42.h index 180d1dd7..6ca7ff03 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid42.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid42.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID42_NumericValueQualifier * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:08 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:42 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid6147.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid6147.h index f4265699..08531b13 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid6147.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid6147.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID6147_ResponseCriteria * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:16 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:49 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid7021.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid7021.h index 749b42f8..e839ed00 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid7021.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid7021.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID7021_MeasurementReportDocumentTitle * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:17 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:50 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid7181.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid7181.h index 2d3f50fb..04a6bb24 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid7181.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid7181.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID7181_AbstractMultiDimensionalImageModelComponentUnit * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:18 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:51 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid7445.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid7445.h index b2b40d18..b0f196ae 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid7445.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid7445.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID7445_DeviceParticipatingRole * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:19 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:52 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid7452.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid7452.h index 5ee59ad0..af61d16b 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid7452.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid7452.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID7452_OrganizationalRole * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:20 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:52 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid7453.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid7453.h index 09227cd2..2444df5b 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid7453.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid7453.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID7453_PerformingRole * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:21 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:53 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid7464.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid7464.h index 481e4e74..a21fd7cf 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid7464.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid7464.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID7464_GeneralRegionOfInterestMeasurementModifier * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:22 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:54 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid7469.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid7469.h index 2b6bc0f6..f01b123f 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid7469.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid7469.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID7469_GenericIntensityAndSizeMeasurement * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:23 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:55 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/cid7551.h b/dcmsr/include/dcmtk/dcmsr/cmr/cid7551.h index eb98b9c3..efb47052 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/cid7551.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/cid7551.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class CID7551_GenericPurposeOfReferenceToImagesAndCoordinatesInMeasurement * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:24 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:56 by J. Riesmeier * */ diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/tid1500.h b/dcmsr/include/dcmtk/dcmsr/cmr/tid1500.h index e34abb8f..5b1bcd39 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/tid1500.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/tid1500.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2022, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class TID1500_MeasurementReport @@ -46,7 +46,7 @@ /** @name specific error conditions for TID 1500 (and included templates) in module dcmsr/cmr */ -//@{ +///@{ /// error: there is no measurement report to add content items to extern DCMTK_CMR_EXPORT const OFConditionConst CMR_EC_NoMeasurementReport; @@ -57,7 +57,7 @@ extern DCMTK_CMR_EXPORT const OFConditionConst CMR_EC_InvalidSegmentationObject; /// error: the given DICOM object is not a real world value mapping object extern DCMTK_CMR_EXPORT const OFConditionConst CMR_EC_InvalidRealWorldValueMappingObject; -//@} +///@} /*---------------------* diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/tid15def.h b/dcmsr/include/dcmtk/dcmsr/cmr/tid15def.h index 26702cb1..2f7ec844 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/tid15def.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/tid15def.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2016-2017, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2016-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for common error constants used in TID 14xx/15xx @@ -32,7 +32,7 @@ /** @name specific error conditions for TID 1500 (and included templates) in module dcmsr/cmr */ -//@{ +///@{ /// error: there is no measurement report to add content items to extern DCMTK_CMR_EXPORT const OFConditionConst CMR_EC_NoMeasurementReport; @@ -45,6 +45,6 @@ extern DCMTK_CMR_EXPORT const OFConditionConst CMR_EC_InvalidSegmentationObject; /// error: the given DICOM object is not a real world value mapping object extern DCMTK_CMR_EXPORT const OFConditionConst CMR_EC_InvalidRealWorldValueMappingObject; -//@} +///@} #endif diff --git a/dcmsr/include/dcmtk/dcmsr/cmr/tid1600.h b/dcmsr/include/dcmtk/dcmsr/cmr/tid1600.h index 95d5bfd3..a34feb44 100644 --- a/dcmsr/include/dcmtk/dcmsr/cmr/tid1600.h +++ b/dcmsr/include/dcmtk/dcmsr/cmr/tid1600.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2021, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file for class TID1600_ImageLibrary @@ -36,7 +36,7 @@ /** @name specific error conditions for TID 1600 in module dcmsr/cmr */ -//@{ +///@{ /// error: there is no image library to add image groups to extern DCMTK_CMR_EXPORT const OFConditionConst CMR_EC_NoImageLibrary; @@ -55,7 +55,7 @@ extern DCMTK_CMR_EXPORT const OFConditionConst CMR_EC_NoImageLibraryEntryDescrip /// normal: there are no (common) image library entry descriptors to be moved (to the image group) extern DCMTK_CMR_EXPORT const OFConditionConst CMR_EC_NoImageLibraryEntryDescriptorsToBeMoved; -//@} +///@} /*---------------------* diff --git a/dcmsr/include/dcmtk/dcmsr/codes/dcm.h b/dcmsr/include/dcmtk/dcmsr/codes/dcm.h index 9fbfac2c..6d2704a4 100644 --- a/dcmsr/include/dcmtk/dcmsr/codes/dcm.h +++ b/dcmsr/include/dcmtk/dcmsr/codes/dcm.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file with DICOM Controlled Terminology Code Definitions (Coding Scheme "DCM", Version "01") * - * Generated automatically from DICOM PS 3.16-2024e - * File created on 2024-11-16 10:17:26 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 11:52:18 by J. Riesmeier * */ @@ -34,8 +34,8 @@ * code definitions * *--------------------*/ -// total number of codes: 4861 -// - retired: 207 +// total number of codes: 5092 +// - retired: 208 // - no name: 27 // - not unique: 17 @@ -143,11 +143,11 @@ #define CODE_DCM_Radiofluoroscopy DSRBasicCodedEntry("RF", "DCM", "Radiofluoroscopy") #define CODE_DCM_RadiographicImaging DSRBasicCodedEntry("RG", "DCM", "Radiographic imaging") #define CODE_DCM_RadiationTherapyDevice DSRBasicCodedEntry("RT", "DCM", "Radiation Therapy Device") -#define CODE_DCM_RadiotherapyDose DSRBasicCodedEntry("RTDOSE", "DCM", "Radiotherapy Dose") -#define CODE_DCM_RadiotherapyImage DSRBasicCodedEntry("RTIMAGE", "DCM", "Radiotherapy Image") -#define CODE_DCM_RadiotherapyPlan DSRBasicCodedEntry("RTPLAN", "DCM", "Radiotherapy Plan") -#define CODE_DCM_RadiotherapyTreatmentRecord DSRBasicCodedEntry("RTRECORD", "DCM", "Radiotherapy Treatment Record") -#define CODE_DCM_RadiotherapyStructureSet DSRBasicCodedEntry("RTSTRUCT", "DCM", "Radiotherapy Structure Set") +#define CODE_DCM_RTDose DSRBasicCodedEntry("RTDOSE", "DCM", "RT Dose") +#define CODE_DCM_RTImage DSRBasicCodedEntry("RTIMAGE", "DCM", "RT Image") +#define CODE_DCM_RTPlan DSRBasicCodedEntry("RTPLAN", "DCM", "RT Plan") +#define CODE_DCM_RTTreatmentRecord DSRBasicCodedEntry("RTRECORD", "DCM", "RT Treatment Record") +#define CODE_DCM_RTStructureSet DSRBasicCodedEntry("RTSTRUCT", "DCM", "RT Structure Set") #define CODE_DCM_RealWorldValueMap DSRBasicCodedEntry("RWV", "DCM", "Real World Value Map") #define CODE_DCM_Segmentation_SEG DSRBasicCodedEntry("SEG", "DCM", "Segmentation") #define CODE_DCM_SlideMicroscopy_SM DSRBasicCodedEntry("SM", "DCM", "Slide Microscopy") @@ -180,8 +180,8 @@ #define CODE_DCM_VoltageMeasurementByBasketCatheter DSRBasicCodedEntry("109011", "DCM", "Voltage measurement by basket catheter") #define CODE_DCM_VoltageMeasurementByMappingCatheter DSRBasicCodedEntry("109012", "DCM", "Voltage measurement by mapping catheter") #define CODE_DCM_VoltageMeasurement DSRBasicCodedEntry("109013", "DCM", "Voltage measurement") -#define CODE_DCM_35PercentOfThermalCO DSRBasicCodedEntry("109014", "DCM", "35% of thermal CO") -#define CODE_DCM_70PercentOfThermalCO DSRBasicCodedEntry("109015", "DCM", "70% of thermal CO") +#define CODE_DCM_35PercentOfThermalDyeDilutionCO DSRBasicCodedEntry("109014", "DCM", "35% of thermal/dye dilution CO") +#define CODE_DCM_70PercentOfThermalDyeDilutionCO DSRBasicCodedEntry("109015", "DCM", "70% of thermal/dye dilution CO") #define CODE_DCM_AWavePeakPressure DSRBasicCodedEntry("109016", "DCM", "A wave peak pressure") #define CODE_DCM_AWavePressure_average DSRBasicCodedEntry("109017", "DCM", "A wave pressure, average") #define CODE_DCM_BeatDetected_accepted DSRBasicCodedEntry("109018", "DCM", "Beat detected (accepted)") @@ -197,7 +197,7 @@ #define CODE_DCM_PeakOfThermalCardiacOutputBolus DSRBasicCodedEntry("109028", "DCM", "Peak of thermal cardiac output bolus") #define CODE_DCM_StartOfExpiration DSRBasicCodedEntry("109029", "DCM", "Start of expiration") #define CODE_DCM_StartOfInspiration DSRBasicCodedEntry("109030", "DCM", "Start of inspiration") -#define CODE_DCM_StartOfThermalCardiacOutputBolus DSRBasicCodedEntry("109031", "DCM", "Start of thermal cardiac output bolus") +#define CODE_DCM_StartOfThermalCO DSRBasicCodedEntry("109031", "DCM", "Start of thermal CO") #define CODE_DCM_RETIRED_SystolicPressure_average DSRBasicCodedEntry("109032", "DCM", "Systolic pressure, average") #define CODE_DCM_RETIRED_SystolicPeakPressure DSRBasicCodedEntry("109033", "DCM", "Systolic peak pressure") #define CODE_DCM_VWavePeakPressure DSRBasicCodedEntry("109034", "DCM", "V wave peak pressure") @@ -669,7 +669,7 @@ #define CODE_DCM_IndividualImpressionRecommendation DSRBasicCodedEntry("111034", "DCM", "Individual Impression/Recommendation") #define CODE_DCM_LesionDensity DSRBasicCodedEntry("111035", "DCM", "Lesion Density") #define CODE_DCM_MammographyCADReport DSRBasicCodedEntry("111036", "DCM", "Mammography CAD Report") -#define CODE_DCM_Margins DSRBasicCodedEntry("111037", "DCM", "Margins") +#define CODE_DCM_RETIRED_Margins DSRBasicCodedEntry("111037", "DCM", "Margins") #define CODE_DCM_NumberOfCalcifications DSRBasicCodedEntry("111038", "DCM", "Number of calcifications") #define CODE_DCM_ObjectType DSRBasicCodedEntry("111039", "DCM", "Object type") #define CODE_DCM_OriginalSource DSRBasicCodedEntry("111040", "DCM", "Original Source") @@ -1342,6 +1342,16 @@ #define CODE_DCM_BarrettUniversalII DSRBasicCodedEntry("111865", "DCM", "Barrett Universal II") #define CODE_DCM_BarrettLensFactor DSRBasicCodedEntry("111866", "DCM", "Barrett Lens Factor") #define CODE_DCM_BarrettDesignFactor DSRBasicCodedEntry("111867", "DCM", "Barrett Design Factor") +#define CODE_DCM_Kane DSRBasicCodedEntry("111868", "DCM", "Kane") +#define CODE_DCM_KaneToric DSRBasicCodedEntry("111869", "DCM", "Kane Toric") +#define CODE_DCM_KaneKeratoconus DSRBasicCodedEntry("111870", "DCM", "Kane Keratoconus") +#define CODE_DCM_BarrettKeratoconus DSRBasicCodedEntry("111871", "DCM", "Barrett Keratoconus") +#define CODE_DCM_BarrettRx DSRBasicCodedEntry("111872", "DCM", "Barrett Rx") +#define CODE_DCM_EVO DSRBasicCodedEntry("111873", "DCM", "EVO") +#define CODE_DCM_ShammasNoHistory DSRBasicCodedEntry("111874", "DCM", "Shammas No-History") +#define CODE_DCM_CamellinCalossi DSRBasicCodedEntry("111875", "DCM", "Camellin-Calossi") +#define CODE_DCM_HillRBF3dot0 DSRBasicCodedEntry("111876", "DCM", "Hill RBF 3.0") +#define CODE_DCM_PEARLDGS DSRBasicCodedEntry("111877", "DCM", "PEARL-DGS") #define CODE_DCM_MaculaCentered DSRBasicCodedEntry("111900", "DCM", "Macula centered") #define CODE_DCM_DiscCentered DSRBasicCodedEntry("111901", "DCM", "Disc centered") #define CODE_DCM_LesionCentered DSRBasicCodedEntry("111902", "DCM", "Lesion centered") @@ -1770,8 +1780,8 @@ #define CODE_DCM_MinimumIntensityProjection DSRBasicCodedEntry("113079", "DCM", "Minimum intensity projection") #define CODE_DCM_GlutamateAndGlutamine DSRBasicCodedEntry("113080", "DCM", "Glutamate and glutamine") #define CODE_DCM_CholineCreatineRatio DSRBasicCodedEntry("113081", "DCM", "Choline/Creatine Ratio") -#define CODE_DCM_NAcetylaspartateCreatineRatio DSRBasicCodedEntry("113082", "DCM", "N-acetylaspartate /Creatine Ratio") -#define CODE_DCM_NAcetylaspartateCholineRatio DSRBasicCodedEntry("113083", "DCM", "N-acetylaspartate /Choline Ratio") +#define CODE_DCM_NAcetylaspartateCreatineRatio DSRBasicCodedEntry("113082", "DCM", "N-acetylaspartate/Creatine Ratio") +#define CODE_DCM_NAcetylaspartateCholineRatio DSRBasicCodedEntry("113083", "DCM", "N-acetylaspartate/Choline Ratio") #define CODE_DCM_Tmax DSRBasicCodedEntry("113084", "DCM", "Tmax") #define CODE_DCM_SpatialResampling DSRBasicCodedEntry("113085", "DCM", "Spatial resampling") #define CODE_DCM_EdgeEnhancement DSRBasicCodedEntry("113086", "DCM", "Edge enhancement") @@ -1784,7 +1794,7 @@ #define CODE_DCM_PolarToRectangularScanConversion DSRBasicCodedEntry("113093", "DCM", "Polar to Rectangular Scan Conversion") #define CODE_DCM_CreatineAndCholine DSRBasicCodedEntry("113094", "DCM", "Creatine and Choline") #define CODE_DCM_LipidAndLactate DSRBasicCodedEntry("113095", "DCM", "Lipid and Lactate") -#define CODE_DCM_CreatinePlusCholineCitrateRatio DSRBasicCodedEntry("113096", "DCM", "Creatine+Choline/ Citrate Ratio") +#define CODE_DCM_CreatinePlusCholineCitrateRatio DSRBasicCodedEntry("113096", "DCM", "Creatine+Choline/Citrate Ratio") #define CODE_DCM_MultiEnergyProportionalWeighting DSRBasicCodedEntry("113097", "DCM", "Multi-energy proportional weighting") #define CODE_DCM_MagnetizationTransferRatio DSRBasicCodedEntry("113098", "DCM", "Magnetization Transfer Ratio") #define CODE_DCM_BasicApplicationConfidentialityProfile DSRBasicCodedEntry("113100", "DCM", "Basic Application Confidentiality Profile") @@ -1802,7 +1812,7 @@ #define CODE_DCM_RetainInstitutionIdentityOption DSRBasicCodedEntry("113112", "DCM", "Retain Institution Identity Option") #define CODE_DCM_PredecessorContainingGroupOfImagingSubjects DSRBasicCodedEntry("113130", "DCM", "Predecessor containing group of imaging subjects") #define CODE_DCM_ExtractionOfIndividualSubjectFromGroup DSRBasicCodedEntry("113131", "DCM", "Extraction of individual subject from group") -#define CODE_DCM_SingleSubjectSelectedFromGroup DSRBasicCodedEntry("113132", "DCM", "Single subject selected from group") +#define CODE_DCM_SingleSubjectExtractedFromGroup DSRBasicCodedEntry("113132", "DCM", "Single subject extracted from group") #define CODE_DCM_Trace DSRBasicCodedEntry("113201", "DCM", "Trace") #define CODE_DCM_MeanDiffusivity DSRBasicCodedEntry("113202", "DCM", "Mean Diffusivity") #define CODE_DCM_RadialDiffusivity DSRBasicCodedEntry("113203", "DCM", "Radial Diffusivity") @@ -1812,13 +1822,13 @@ #define CODE_DCM_RadialKurtosis DSRBasicCodedEntry("113207", "DCM", "Radial Kurtosis") #define CODE_DCM_AxialKurtosis DSRBasicCodedEntry("113208", "DCM", "Axial Kurtosis") #define CODE_DCM_FractionalKurtosisAnisotropy DSRBasicCodedEntry("113209", "DCM", "Fractional Kurtosis Anisotropy") -#define CODE_DCM_DeterministicTrackingAlgorithm DSRBasicCodedEntry("113211", "DCM", "Deterministic Tracking Algorithm") -#define CODE_DCM_ProbabilisticTrackingAlgorithm DSRBasicCodedEntry("113212", "DCM", "Probabilistic Tracking Algorithm") -#define CODE_DCM_GlobalTrackingAlgorithm DSRBasicCodedEntry("113213", "DCM", "Global Tracking Algorithm") +#define CODE_DCM_Deterministic DSRBasicCodedEntry("113211", "DCM", "Deterministic") +#define CODE_DCM_Probabilistic DSRBasicCodedEntry("113212", "DCM", "Probabilistic") +#define CODE_DCM_Global DSRBasicCodedEntry("113213", "DCM", "Global") #define CODE_DCM_FACT DSRBasicCodedEntry("113214", "DCM", "FACT") #define CODE_DCM_Streamline DSRBasicCodedEntry("113215", "DCM", "Streamline") #define CODE_DCM_TEND DSRBasicCodedEntry("113216", "DCM", "TEND") -#define CODE_DCM_BootstrapTrackingAlgorithm DSRBasicCodedEntry("113217", "DCM", "Bootstrap Tracking Algorithm") +#define CODE_DCM_Bootstrap DSRBasicCodedEntry("113217", "DCM", "Bootstrap") #define CODE_DCM_Euler DSRBasicCodedEntry("113218", "DCM", "Euler") #define CODE_DCM_RungeKutta DSRBasicCodedEntry("113219", "DCM", "Runge-Kutta") #define CODE_DCM_HARDI DSRBasicCodedEntry("113221", "DCM", "HARDI") @@ -1827,7 +1837,7 @@ #define CODE_DCM_DSI DSRBasicCodedEntry("113224", "DCM", "DSI") #define CODE_DCM_LSDI DSRBasicCodedEntry("113225", "DCM", "LSDI") #define CODE_DCM_SingleShotEPI DSRBasicCodedEntry("113226", "DCM", "Single Shot EPI") -#define CODE_DCM_MultiShotEPI DSRBasicCodedEntry("113227", "DCM", "Multi Shot EPI") +#define CODE_DCM_MultipleShotEPI DSRBasicCodedEntry("113227", "DCM", "Multiple Shot EPI") #define CODE_DCM_ParallelImaging DSRBasicCodedEntry("113228", "DCM", "Parallel Imaging") #define CODE_DCM_SingleTensor DSRBasicCodedEntry("113231", "DCM", "Single Tensor") #define CODE_DCM_MultiTensor DSRBasicCodedEntry("113232", "DCM", "Multi Tensor") @@ -1885,6 +1895,8 @@ #define CODE_DCM_ICRPPublication53 DSRBasicCodedEntry("113521", "DCM", "ICRP Publication 53") #define CODE_DCM_ICRPPublication80 DSRBasicCodedEntry("113522", "DCM", "ICRP Publication 80") #define CODE_DCM_ICRPPublication106 DSRBasicCodedEntry("113523", "DCM", "ICRP Publication 106") +#define CODE_DCM_ICRPPublication128 DSRBasicCodedEntry("113524", "DCM", "ICRP Publication 128") +#define CODE_DCM_RADAR2017 DSRBasicCodedEntry("113525", "DCM", "RADAR 2017") #define CODE_DCM_MIRDOSE DSRBasicCodedEntry("113526", "DCM", "MIRDOSE") #define CODE_DCM_OLINDAEXM DSRBasicCodedEntry("113527", "DCM", "OLINDA-EXM") #define CODE_DCM_PackageInsert DSRBasicCodedEntry("113528", "DCM", "Package Insert") @@ -1893,7 +1905,7 @@ #define CODE_DCM_ActivityMeasurementDevice DSRBasicCodedEntry("113540", "DCM", "Activity Measurement Device") #define CODE_DCM_DoseCalibrator DSRBasicCodedEntry("113541", "DCM", "Dose Calibrator") #define CODE_DCM_InfusionSystem DSRBasicCodedEntry("113542", "DCM", "Infusion System") -#define CODE_DCM_Generator DSRBasicCodedEntry("113543", "DCM", "Generator") +#define CODE_DCM_RadioisotopeGenerator DSRBasicCodedEntry("113543", "DCM", "Radioisotope Generator") #define CODE_DCM_FastingDuration DSRBasicCodedEntry("113550", "DCM", "Fasting Duration") #define CODE_DCM_HydrationVolume DSRBasicCodedEntry("113551", "DCM", "Hydration Volume") #define CODE_DCM_RecentPhysicalActivity DSRBasicCodedEntry("113552", "DCM", "Recent Physical Activity") @@ -1949,8 +1961,8 @@ #define CODE_DCM_ACRAccreditationPhantom_PET DSRBasicCodedEntry("113687", "DCM", "ACR Accreditation Phantom - PET") #define CODE_DCM_ACRAccreditationPhantom_ECTPET DSRBasicCodedEntry("113688", "DCM", "ACR Accreditation Phantom - ECT/PET") #define CODE_DCM_ACRAccreditationPhantom_PETFaceplate DSRBasicCodedEntry("113689", "DCM", "ACR Accreditation Phantom - PET Faceplate") -#define CODE_DCM_IECHeadDosimetryPhantom DSRBasicCodedEntry("113690", "DCM", "IEC Head Dosimetry Phantom") -#define CODE_DCM_IECBodyDosimetryPhantom DSRBasicCodedEntry("113691", "DCM", "IEC Body Dosimetry Phantom") +#define CODE_DCM_IEC160mmHeadCTDosimetryPhantom DSRBasicCodedEntry("113690", "DCM", "IEC 160mm Head CT Dosimetry Phantom") +#define CODE_DCM_IEC320mmBodyCTDosimetryPhantom DSRBasicCodedEntry("113691", "DCM", "IEC 320mm Body CT Dosimetry Phantom") #define CODE_DCM_NEMAXR212000Phantom DSRBasicCodedEntry("113692", "DCM", "NEMA XR21-2000 Phantom") #define CODE_DCM_XRayRadiationDoseReport DSRBasicCodedEntry("113701", "DCM", "X-Ray Radiation Dose Report") #define CODE_DCM_AccumulatedXRayDoseData DSRBasicCodedEntry("113702", "DCM", "Accumulated X-Ray Dose Data") @@ -2319,10 +2331,10 @@ #define CODE_DCM_PerformedProcedureStepSOPInstanceUID DSRBasicCodedEntry("121126", "DCM", "Performed Procedure Step SOP Instance UID") #define CODE_DCM_PerformedProcedureStepSOPClassUID DSRBasicCodedEntry("121127", "DCM", "Performed Procedure Step SOP Class UID") #define CODE_DCM_ProcedureActionDuration DSRBasicCodedEntry("121128", "DCM", "Procedure Action Duration") -#define CODE_DCM_StartProcedureActionItem DSRBasicCodedEntry("121130", "DCM", "Start Procedure Action Item") -#define CODE_DCM_EndProcedureActionItem DSRBasicCodedEntry("121131", "DCM", "End Procedure Action Item") -#define CODE_DCM_SuspendProcedureActionItem DSRBasicCodedEntry("121132", "DCM", "Suspend Procedure Action Item") -#define CODE_DCM_ResumeProcedureActionItem DSRBasicCodedEntry("121133", "DCM", "Resume Procedure Action Item") +#define CODE_DCM_StartProcedureAction DSRBasicCodedEntry("121130", "DCM", "Start Procedure Action") +#define CODE_DCM_EndProcedureAction DSRBasicCodedEntry("121131", "DCM", "End Procedure Action") +#define CODE_DCM_SuspendProcedureAction DSRBasicCodedEntry("121132", "DCM", "Suspend Procedure Action") +#define CODE_DCM_ResumeProcedureAction DSRBasicCodedEntry("121133", "DCM", "Resume Procedure Action") #define CODE_DCM_ObservationDateTimeQualifier DSRBasicCodedEntry("121135", "DCM", "Observation DateTime Qualifier") #define CODE_DCM_DateTimeUnsynchronized DSRBasicCodedEntry("121136", "DCM", "DateTime Unsynchronized") #define CODE_DCM_DateTimeEstimated DSRBasicCodedEntry("121137", "DCM", "DateTime Estimated") @@ -2350,7 +2362,7 @@ #define CODE_DCM_OxygenAdministrationRate DSRBasicCodedEntry("121160", "DCM", "Oxygen Administration Rate") #define CODE_DCM_BeginOxygenAdministration DSRBasicCodedEntry("121161", "DCM", "Begin Oxygen Administration") #define CODE_DCM_EndOxygenAdministration DSRBasicCodedEntry("121162", "DCM", "End oxygen administration") -#define CODE_DCM_ByVentilator DSRBasicCodedEntry("121163", "DCM", "By ventilator") +#define CODE_DCM_OxygenAdministrationByVentilator DSRBasicCodedEntry("121163", "DCM", "Oxygen Administration by ventilator") #define CODE_DCM_PatientAssessmentPerformed DSRBasicCodedEntry("121165", "DCM", "Patient Assessment Performed") #define CODE_DCM_BeginPacing DSRBasicCodedEntry("121166", "DCM", "Begin Pacing") #define CODE_DCM_EndPacing DSRBasicCodedEntry("121167", "DCM", "End Pacing") @@ -2453,6 +2465,9 @@ #define CODE_DCM_AddendedReport DSRBasicCodedEntry("121361", "DCM", "Addended report") #define CODE_DCM_PreliminaryReport DSRBasicCodedEntry("121362", "DCM", "Preliminary report") #define CODE_DCM_PartialReport DSRBasicCodedEntry("121363", "DCM", "Partial report") +#define CODE_DCM_ComposedFromDeformableRegistration DSRBasicCodedEntry("121367", "DCM", "Composed from deformable registration") +#define CODE_DCM_ComposedFromImageSeriesOtherThanPlanningImageSeries DSRBasicCodedEntry("121368", "DCM", "Composed from image series other than planning image series") +#define CODE_DCM_ComposedFromSetupPerturbation DSRBasicCodedEntry("121369", "DCM", "Composed from setup perturbation") #define CODE_DCM_ComposedFromPriorDoses DSRBasicCodedEntry("121370", "DCM", "Composed from prior doses") #define CODE_DCM_ComposedFromPriorDosesAndCurrentPlan DSRBasicCodedEntry("121371", "DCM", "Composed from prior doses and current plan") #define CODE_DCM_SourceDoseForComposingCurrentDose DSRBasicCodedEntry("121372", "DCM", "Source dose for composing current dose") @@ -2472,6 +2487,7 @@ #define CODE_DCM_NumberOfFractionsCompleted DSRBasicCodedEntry("121387", "DCM", "Number of Fractions Completed") #define CODE_DCM_CheckedInStatus DSRBasicCodedEntry("121388", "DCM", "Checked-In Status") #define CODE_DCM_ReferencedBeamNumber DSRBasicCodedEntry("121389", "DCM", "Referenced Beam Number") +#define CODE_DCM_ClinicalFractionNumber DSRBasicCodedEntry("121390", "DCM", "Clinical Fraction Number") #define CODE_DCM_Derivation DSRBasicCodedEntry("121401", "DCM", "Derivation") #define CODE_DCM_Normality DSRBasicCodedEntry("121402", "DCM", "Normality") #define CODE_DCM_LevelOfSignificance DSRBasicCodedEntry("121403", "DCM", "Level of Significance") @@ -2538,7 +2554,7 @@ #define CODE_DCM_RTTreatmentQAByRTPlanDoseCheck DSRBasicCodedEntry("121731", "DCM", "RT Treatment QA by RT Plan Dose Check") #define CODE_DCM_RTTreatmentQAByRTPlanDifferenceCheck DSRBasicCodedEntry("121732", "DCM", "RT Treatment QA by RT Plan Difference Check") #define CODE_DCM_RTTreatmentQAByRTIonPlanDoseCheck DSRBasicCodedEntry("121733", "DCM", "RT Treatment QA by RT Ion Plan Dose Check") -#define CODE_DCM_RTTreatmentQAWithRTIonPlanDifferenceCheck DSRBasicCodedEntry("121734", "DCM", "RT Treatment QA with RT Ion Plan Difference Check") +#define CODE_DCM_RTTreatmentQAByRTIonPlanDifferenceCheck DSRBasicCodedEntry("121734", "DCM", "RT Treatment QA by RT Ion Plan Difference Check") #define CODE_DCM_RTBrachyTreatment DSRBasicCodedEntry("121735", "DCM", "RT Brachy Treatment") #define CODE_DCM_RTImageGuidedPatientPositioningAndTreatmentDelivery DSRBasicCodedEntry("121736", "DCM", "RT Image-Guided Patient Positioning and Treatment Delivery") #define CODE_DCM_RTPatientPositionAcquisition_MR DSRBasicCodedEntry("121737", "DCM", "RT Patient Position Acquisition, MR") @@ -2671,7 +2687,7 @@ #define CODE_DCM_Probability DSRBasicCodedEntry("122157", "DCM", "Probability") #define CODE_DCM_ECGGlobalMeasurements DSRBasicCodedEntry("122158", "DCM", "ECG Global Measurements") #define CODE_DCM_ECGLeadMeasurements DSRBasicCodedEntry("122159", "DCM", "ECG Lead Measurements") -#define CODE_DCM_DerivedArea_NonValve DSRBasicCodedEntry("122160", "DCM", "Derived Area, Non-Valve") +#define CODE_DCM_DerivedNonValveArea DSRBasicCodedEntry("122160", "DCM", "Derived Non-Valve Area") #define CODE_DCM_PulmonaryFlow DSRBasicCodedEntry("122161", "DCM", "Pulmonary Flow") #define CODE_DCM_SystemicFlow DSRBasicCodedEntry("122162", "DCM", "Systemic Flow") #define CODE_DCM_DischargeDateTime DSRBasicCodedEntry("122163", "DCM", "Discharge DateTime") @@ -2898,7 +2914,7 @@ #define CODE_DCM_TerritoryRegionSeverity DSRBasicCodedEntry("122459", "DCM", "Territory Region Severity") #define CODE_DCM_OppositeRegionSeverity DSRBasicCodedEntry("122461", "DCM", "Opposite Region Severity") #define CODE_DCM_LADRegionInRAOProjection DSRBasicCodedEntry("122464", "DCM", "LAD Region in RAO Projection") -#define CODE_DCM_RCARegionInROAProjection DSRBasicCodedEntry("122465", "DCM", "RCA Region in ROA Projection") +#define CODE_DCM_RCARegionInRAOProjection DSRBasicCodedEntry("122465", "DCM", "RCA Region in RAO Projection") #define CODE_DCM_SingleLADRegionInRAOProjection DSRBasicCodedEntry("122466", "DCM", "Single LAD Region in RAO Projection") #define CODE_DCM_SingleRCARegionInRAOProjection DSRBasicCodedEntry("122467", "DCM", "Single RCA Region in RAO Projection") #define CODE_DCM_MultipleLADRegionInRAOProjection DSRBasicCodedEntry("122468", "DCM", "Multiple LAD Region in RAO Projection") @@ -3070,7 +3086,7 @@ #define CODE_DCM_STDepression_Downsloping DSRBasicCodedEntry("122759", "DCM", "ST Depression - Downsloping") #define CODE_DCM_StressTestScore DSRBasicCodedEntry("122760", "DCM", "Stress test score") #define CODE_DCM_NumberOfDiseasedVesselTerritories DSRBasicCodedEntry("122762", "DCM", "Number of diseased vessel territories") -#define CODE_DCM_WeightExceedsEquipmentLimit DSRBasicCodedEntry("122764", "DCM", "Weight exceeds equipment limit") +#define CODE_DCM_PatientWeightExceedsEquipmentLimit DSRBasicCodedEntry("122764", "DCM", "Patient weight exceeds equipment limit") #define CODE_DCM_DifferenceInEjectionFraction DSRBasicCodedEntry("122768", "DCM", "Difference in Ejection Fraction") #define CODE_DCM_DifferenceInEDLVVolume DSRBasicCodedEntry("122769", "DCM", "Difference in ED LV Volume") #define CODE_DCM_RatioOfAchievedToPredictedMaximalOxygenConsumption DSRBasicCodedEntry("122770", "DCM", "Ratio of achieved to predicted maximal oxygen consumption") @@ -3166,7 +3182,7 @@ #define CODE_DCM_AcquisitionProtocol DSRBasicCodedEntry("125203", "DCM", "Acquisition Protocol") #define CODE_DCM_AreaLengthBiplane DSRBasicCodedEntry("125204", "DCM", "Area-length biplane") #define CODE_DCM_AreaLengthSinglePlane DSRBasicCodedEntry("125205", "DCM", "Area-Length Single Plane") -#define CODE_DCM_Cube DSRBasicCodedEntry("125206", "DCM", "Cube") +#define CODE_DCM_CubeMethod DSRBasicCodedEntry("125206", "DCM", "Cube Method") #define CODE_DCM_MethodOfDisks_Biplane DSRBasicCodedEntry("125207", "DCM", "Method of Disks, Biplane") #define CODE_DCM_MethodOfDisks_SinglePlane DSRBasicCodedEntry("125208", "DCM", "Method of Disks, Single Plane") #define CODE_DCM_Teichholz DSRBasicCodedEntry("125209", "DCM", "Teichholz") @@ -3280,8 +3296,8 @@ #define CODE_DCM_Glycolysis DSRBasicCodedEntry("126034", "DCM", "Glycolysis") #define CODE_DCM_TotalLesionProliferation DSRBasicCodedEntry("126035", "DCM", "Total Lesion Proliferation") #define CODE_DCM_ProliferativeActivity DSRBasicCodedEntry("126036", "DCM", "Proliferative Activity") -#define CODE_DCM_StandardizedAddedMetabolicActivity_SAM DSRBasicCodedEntry("126037", "DCM", "Standardized Added Metabolic Activity (SAM)") -#define CODE_DCM_StandardizedAddedMetabolicActivity_SAM_Background DSRBasicCodedEntry("126038", "DCM", "Standardized Added Metabolic Activity (SAM) Background") +#define CODE_DCM_StandardizedAddedMetabolicActivity DSRBasicCodedEntry("126037", "DCM", "Standardized Added Metabolic Activity") +#define CODE_DCM_StandardizedAddedMetabolicActivityBackground DSRBasicCodedEntry("126038", "DCM", "Standardized Added Metabolic Activity Background") #define CODE_DCM_LesionToBackgroundSUVRatio DSRBasicCodedEntry("126039", "DCM", "Lesion to Background SUV Ratio") #define CODE_DCM_BackgroundForLesionToBackgroundSUVRatio DSRBasicCodedEntry("126040", "DCM", "Background for Lesion to Background SUV Ratio") #define CODE_DCM_FractalDimension DSRBasicCodedEntry("126050", "DCM", "Fractal Dimension") @@ -3317,8 +3333,8 @@ #define CODE_DCM_PerfusionAnalysisByIVIodinatedContrastCTTechnique DSRBasicCodedEntry("126301", "DCM", "Perfusion analysis by IV Iodinated Contrast CT technique") #define CODE_DCM_PerfusionAnalysisByArterialSpinLabelingMRTechnique DSRBasicCodedEntry("126302", "DCM", "Perfusion analysis by Arterial Spin Labeling MR technique") #define CODE_DCM_PerfusionAnalysisBySusceptibilityMRTechnique DSRBasicCodedEntry("126303", "DCM", "Perfusion analysis by Susceptibility MR technique") -#define CODE_DCM_LeastMeanSquare_LMS_deconvolution DSRBasicCodedEntry("126310", "DCM", "Least Mean Square (LMS) deconvolution") -#define CODE_DCM_SingularValueDecomposition_SVD_deconvolution DSRBasicCodedEntry("126311", "DCM", "Singular Value Decomposition (SVD) deconvolution") +#define CODE_DCM_LeastMeanSquareDeconvolution DSRBasicCodedEntry("126310", "DCM", "Least Mean Square deconvolution") +#define CODE_DCM_SingularValueDecompositionDeconvolution DSRBasicCodedEntry("126311", "DCM", "Singular Value Decomposition deconvolution") #define CODE_DCM_Ktrans DSRBasicCodedEntry("126312", "DCM", "Ktrans") #define CODE_DCM_Kep DSRBasicCodedEntry("126313", "DCM", "kep") #define CODE_DCM_Ve DSRBasicCodedEntry("126314", "DCM", "ve") @@ -3327,19 +3343,19 @@ #define CODE_DCM_IAUC90 DSRBasicCodedEntry("126322", "DCM", "IAUC90") #define CODE_DCM_IAUC180 DSRBasicCodedEntry("126323", "DCM", "IAUC180") #define CODE_DCM_IAUCBN DSRBasicCodedEntry("126324", "DCM", "IAUCBN") -#define CODE_DCM_IAUCBN60 DSRBasicCodedEntry("126325", "DCM", "IAUCBN60") -#define CODE_DCM_IAUCBN90 DSRBasicCodedEntry("126326", "DCM", "IAUCBN90") -#define CODE_DCM_AUCBN180 DSRBasicCodedEntry("126327", "DCM", "AUCBN180") +#define CODE_DCM_IAUC60BN DSRBasicCodedEntry("126325", "DCM", "IAUC60BN") +#define CODE_DCM_IAUC90BN DSRBasicCodedEntry("126326", "DCM", "IAUC90BN") +#define CODE_DCM_IAUC180BN DSRBasicCodedEntry("126327", "DCM", "IAUC180BN") #define CODE_DCM_Tau_m DSRBasicCodedEntry("126330", "DCM", "tau_m") #define CODE_DCM_Vp DSRBasicCodedEntry("126331", "DCM", "vp") #define CODE_DCM_StandardToftsModel DSRBasicCodedEntry("126340", "DCM", "Standard Tofts Model") #define CODE_DCM_ExtendedToftsModel DSRBasicCodedEntry("126341", "DCM", "Extended Tofts Model") #define CODE_DCM_ModelFreeConcentrationTimeQuantitification DSRBasicCodedEntry("126342", "DCM", "Model-free concentration-time quantitification") -#define CODE_DCM_FirstPassLeakageProfile_FPLP_Model DSRBasicCodedEntry("126343", "DCM", "First Pass Leakage Profile (FPLP) Model") -#define CODE_DCM_ShutterSpeedModel_SSM DSRBasicCodedEntry("126344", "DCM", "Shutter-Speed Model (SSM)") -#define CODE_DCM_GammaCapillaryTransitTime_GCCT_Model DSRBasicCodedEntry("126345", "DCM", "Gamma Capillary Transit Time (GCCT) Model") -#define CODE_DCM_AdiabaticTissueHomogeneity_ATH_Model DSRBasicCodedEntry("126346", "DCM", "Adiabatic Tissue Homogeneity (ATH) Model") -#define CODE_DCM_TwoCompartmentExchange_2CX_Model DSRBasicCodedEntry("126347", "DCM", "Two Compartment Exchange (2CX) Model") +#define CODE_DCM_FirstPassLeakageProfileModel DSRBasicCodedEntry("126343", "DCM", "First Pass Leakage Profile Model") +#define CODE_DCM_ShutterSpeedModel DSRBasicCodedEntry("126344", "DCM", "Shutter-Speed Model") +#define CODE_DCM_GammaCapillaryTransitTimeModel DSRBasicCodedEntry("126345", "DCM", "Gamma Capillary Transit Time Model") +#define CODE_DCM_AdiabaticTissueHomogeneityModel DSRBasicCodedEntry("126346", "DCM", "Adiabatic Tissue Homogeneity Model") +#define CODE_DCM_TwoCompartmentExchangeModel DSRBasicCodedEntry("126347", "DCM", "Two Compartment Exchange Model") #define CODE_DCM_T1ByMultipleFlipAngles DSRBasicCodedEntry("126350", "DCM", "T1 by Multiple Flip Angles") #define CODE_DCM_T1ByInversionRecovery DSRBasicCodedEntry("126351", "DCM", "T1 by Inversion Recovery") #define CODE_DCM_T1ByFixedValue DSRBasicCodedEntry("126352", "DCM", "T1 by Fixed Value") @@ -3385,8 +3401,8 @@ #define CODE_DCM_T807_F18 DSRBasicCodedEntry("126502", "DCM", "T807 F^18^") #define CODE_DCM_Flubatine_F18 DSRBasicCodedEntry("126503", "DCM", "Flubatine F^18^") #define CODE_DCM_Lutetium177NAcetylaspartylglutamate DSRBasicCodedEntry("126509", "DCM", "Lutetium^177^ n-acetylaspartylglutamate") -#define CODE_DCM_MonoclonalAntibody_mAb_64Cu DSRBasicCodedEntry("126510", "DCM", "Monoclonal Antibody (mAb) ^64^Cu") -#define CODE_DCM_MonoclonalAntibody_mAb_89Zr DSRBasicCodedEntry("126511", "DCM", "Monoclonal Antibody (mAb) ^89^Zr") +#define CODE_DCM_MonoclonalAntibody_64Cu DSRBasicCodedEntry("126510", "DCM", "Monoclonal Antibody ^64^Cu") +#define CODE_DCM_MonoclonalAntibody_89Zr DSRBasicCodedEntry("126511", "DCM", "Monoclonal Antibody ^89^Zr") #define CODE_DCM_Trastuzumab_89Zr DSRBasicCodedEntry("126512", "DCM", "Trastuzumab ^89^Zr") #define CODE_DCM_Cetuximab_89Zr DSRBasicCodedEntry("126513", "DCM", "Cetuximab ^89^Zr") #define CODE_DCM_J591_89Zr DSRBasicCodedEntry("126514", "DCM", "J591 ^89^Zr") @@ -3396,6 +3412,7 @@ #define CODE_DCM_R1507_89Zr DSRBasicCodedEntry("126518", "DCM", "R1507 ^89^Zr") #define CODE_DCM_E4G10_89Zr DSRBasicCodedEntry("126519", "DCM", "E4G10 ^89^Zr") #define CODE_DCM_DfCD45_89Zr DSRBasicCodedEntry("126520", "DCM", "Df-CD45 ^89^Zr") +#define CODE_DCM_Glucose_C11 DSRBasicCodedEntry("126521", "DCM", "Glucose C^11^") #define CODE_DCM_44Scandium DSRBasicCodedEntry("126600", "DCM", "^44^Scandium") #define CODE_DCM_51Manganese DSRBasicCodedEntry("126601", "DCM", "^51^Manganese") #define CODE_DCM_70Arsenic DSRBasicCodedEntry("126602", "DCM", "^70^Arsenic") @@ -3617,7 +3634,7 @@ #define CODE_DCM_HeadAndNeckImagingSpecialty DSRBasicCodedEntry("128008", "DCM", "Head and Neck Imaging Specialty") #define CODE_DCM_MusculoskeletalImagingSpecialty DSRBasicCodedEntry("128009", "DCM", "Musculoskeletal Imaging Specialty") #define CODE_DCM_NeurologySpecialty DSRBasicCodedEntry("128010", "DCM", "Neurology Specialty") -#define CODE_DCM_NeuroradiologicImagingSpecialty DSRBasicCodedEntry("128011", "DCM", "Neuroradiologic Imaging Specialty") +#define CODE_DCM_NeuroradiologyImagingSpecialty DSRBasicCodedEntry("128011", "DCM", "Neuroradiology Imaging Specialty") #define CODE_DCM_OBGynImagingSpecialty DSRBasicCodedEntry("128012", "DCM", "OB/Gyn Imaging Specialty") #define CODE_DCM_OncologicImagingSpecialty DSRBasicCodedEntry("128013", "DCM", "Oncologic Imaging Specialty") #define CODE_DCM_OncologySpecialty DSRBasicCodedEntry("128014", "DCM", "Oncology Specialty") @@ -3845,7 +3862,7 @@ #define CODE_DCM_SkinDoseMap DSRBasicCodedEntry("128485", "DCM", "Skin Dose Map") #define CODE_DCM_3DDoseMap DSRBasicCodedEntry("128487", "DCM", "3D Dose Map") #define CODE_DCM_DoseGradient DSRBasicCodedEntry("128488", "DCM", "Dose Gradient") -#define CODE_DCM_PhysicalSupport DSRBasicCodedEntry("128492", "DCM", "Physical Support") +#define CODE_DCM_PatientSupport DSRBasicCodedEntry("128492", "DCM", "Patient Support") #define CODE_DCM_PatientSegmentedModel DSRBasicCodedEntry("128494", "DCM", "Patient Segmented Model") #define CODE_DCM_DosePointCloud DSRBasicCodedEntry("128496", "DCM", "Dose Point Cloud") #define CODE_DCM_MeasuredRadiationDose DSRBasicCodedEntry("128497", "DCM", "Measured Radiation Dose") @@ -4117,7 +4134,7 @@ #define CODE_DCM_FixationOrPositioningDevice DSRBasicCodedEntry("130044", "DCM", "Fixation or Positioning Device") #define CODE_DCM_BrachytherapyDevice DSRBasicCodedEntry("130045", "DCM", "Brachytherapy Device") #define CODE_DCM_NonSpecificVolume DSRBasicCodedEntry("130046", "DCM", "Non-specific Volume") -#define CODE_DCM_ExternalBodyStructure DSRBasicCodedEntry("130047", "DCM", "External Body Structure") +#define CODE_DCM_ExternalBodyModel DSRBasicCodedEntry("130047", "DCM", "External Body Model") #define CODE_DCM_UnclassifiedVolume DSRBasicCodedEntry("130048", "DCM", "Unclassified Volume") #define CODE_DCM_CTVNodal DSRBasicCodedEntry("130049", "DCM", "CTV Nodal") #define CODE_DCM_CTVPrimary DSRBasicCodedEntry("130050", "DCM", "CTV Primary") @@ -4349,6 +4366,7 @@ #define CODE_DCM_SkinOfUpperEyelidMargin DSRBasicCodedEntry("130322", "DCM", "Skin of upper eyelid margin") #define CODE_DCM_SkinOfMidBack DSRBasicCodedEntry("130323", "DCM", "Skin of mid back") #define CODE_DCM_FunctionalConditionPresentDuringAcquisition DSRBasicCodedEntry("130324", "DCM", "Functional condition present during acquisition") +#define CODE_DCM_OrthognathicFunctionalCondition DSRBasicCodedEntry("130325", "DCM", "Orthognathic Functional Condition") #define CODE_DCM_JawPair DSRBasicCodedEntry("130330", "DCM", "Jaw Pair") #define CODE_DCM_LeafPairs DSRBasicCodedEntry("130331", "DCM", "Leaf Pairs") #define CODE_DCM_VariableCircularCollimator DSRBasicCodedEntry("130332", "DCM", "Variable Circular Collimator") @@ -4442,8 +4460,8 @@ #define CODE_DCM_PatientDecisionToTerminateTreatment DSRBasicCodedEntry("130451", "DCM", "Patient decision to terminate treatment") #define CODE_DCM_PhysicianDecisionToTerminateTreatment DSRBasicCodedEntry("130452", "DCM", "Physician decision to terminate treatment") #define CODE_DCM_TreatmentTerminated DSRBasicCodedEntry("130453", "DCM", "Treatment Terminated") -#define CODE_DCM_ResolvedByOverridingInterlock DSRBasicCodedEntry("130454", "DCM", "Resolved by overriding Interlock") -#define CODE_DCM_ResolvedByRepositioningPatient DSRBasicCodedEntry("130455", "DCM", "Resolved by repositioning Patient") +#define CODE_DCM_InterlockOverridden DSRBasicCodedEntry("130454", "DCM", "Interlock Overridden") +#define CODE_DCM_PatientRepositioned DSRBasicCodedEntry("130455", "DCM", "Patient Repositioned") #define CODE_DCM_BolusPresent DSRBasicCodedEntry("130456", "DCM", "Bolus Present") #define CODE_DCM_ConePresent DSRBasicCodedEntry("130457", "DCM", "Cone Present") #define CODE_DCM_BlockPresent DSRBasicCodedEntry("130458", "DCM", "Block Present") @@ -4525,7 +4543,7 @@ #define CODE_DCM_PatientSupportOrigin DSRBasicCodedEntry("130538", "DCM", "Patient Support Origin") #define CODE_DCM_IsocenterOrigin DSRBasicCodedEntry("130539", "DCM", "Isocenter Origin") #define CODE_DCM_PatientCoordinateSystemOrigin DSRBasicCodedEntry("130540", "DCM", "Patient Coordinate System Origin") -#define CODE_DCM_10cmDosimetryPhantom DSRBasicCodedEntry("130541", "DCM", "10 cm Dosimetry Phantom") +#define CODE_DCM_100mmPediatricHeadCTDosimetryPhantom DSRBasicCodedEntry("130541", "DCM", "100 mm Pediatric Head CT Dosimetry Phantom") #define CODE_DCM_MagneticFieldStrength DSRBasicCodedEntry("130542", "DCM", "Magnetic field strength") #define CODE_DCM_EndorectalCoilUsed DSRBasicCodedEntry("130543", "DCM", "Endorectal coil used") #define CODE_DCM_EndorectalCoilType DSRBasicCodedEntry("130544", "DCM", "Endorectal coil type") @@ -4684,7 +4702,7 @@ #define CODE_DCM_O9 DSRBasicCodedEntry("130716", "DCM", "O9") #define CODE_DCM_O10 DSRBasicCodedEntry("130717", "DCM", "O10") #define CODE_DCM_CoronaryArteryDiseaseAssessment DSRBasicCodedEntry("130720", "DCM", "Coronary Artery Disease Assessment") -#define CODE_DCM_OronaryArteryDiseaseAssessmentModifier DSRBasicCodedEntry("130721", "DCM", "oronary Artery Disease Assessment Modifier") +#define CODE_DCM_CoronaryArteryDiseaseAssessmentModifier DSRBasicCodedEntry("130721", "DCM", "Coronary Artery Disease Assessment Modifier") #define CODE_DCM_CoronaryArteryDiseaseStenosisAssessmentModifier DSRBasicCodedEntry("130722", "DCM", "Coronary Artery Disease Stenosis Assessment Modifier") #define CODE_DCM_0_DocumentedAbsenceOfCAD DSRBasicCodedEntry("130723", "DCM", "0 - Documented absence of CAD") #define CODE_DCM_1_MinimalNonObstructiveCAD DSRBasicCodedEntry("130724", "DCM", "1 - Minimal non-obstructive CAD") @@ -4909,5 +4927,218 @@ #define CODE_DCM_TricuspidValveAnnulusDiameter DSRBasicCodedEntry("131061", "DCM", "Tricuspid valve annulus diameter") #define CODE_DCM_IVCSWavePeakVelocity DSRBasicCodedEntry("131062", "DCM", "IVC S-wave peak velocity") #define CODE_DCM_IVCAWavePeakVelocity DSRBasicCodedEntry("131063", "DCM", "IVC a-wave peak velocity") +#define CODE_DCM_PreProceduralStructuralHeartMeasurementReport DSRBasicCodedEntry("131090", "DCM", "Pre-procedural Structural Heart Measurement Report") +#define CODE_DCM_IntraProceduralStructuralHeartMeasurementReport DSRBasicCodedEntry("131091", "DCM", "Intra-procedural Structural Heart Measurement Report") +#define CODE_DCM_PostProceduralStructuralHeartMeasurementReport DSRBasicCodedEntry("131092", "DCM", "Post-procedural Structural Heart Measurement Report") +#define CODE_DCM_MidEsophageal0DegreeTEE DSRBasicCodedEntry("131100", "DCM", "Mid-esophageal 0 degree TEE") +#define CODE_DCM_MidEsophageal45DegreeTEE DSRBasicCodedEntry("131101", "DCM", "Mid-esophageal 45 degree TEE") +#define CODE_DCM_MidEsophageal60DegreeTEE DSRBasicCodedEntry("131102", "DCM", "Mid-esophageal 60 degree TEE") +#define CODE_DCM_MidEsophageal90DegreeTEE DSRBasicCodedEntry("131103", "DCM", "Mid-esophageal 90 degree TEE") +#define CODE_DCM_MidEsophageal135DegreeTEE DSRBasicCodedEntry("131104", "DCM", "Mid-esophageal 135 degree TEE") +#define CODE_DCM_BallAndDiskTypeLeftAtrialAppendageClosureDevice DSRBasicCodedEntry("131110", "DCM", "Ball and disk type left atrial appendage closure device") +#define CODE_DCM_BallTypeLeftAtrialAppendageClosureDevice DSRBasicCodedEntry("131111", "DCM", "Ball type left atrial appendage closure device") +#define CODE_DCM_LeftAtrialAppendageClosureDevice DSRBasicCodedEntry("131112", "DCM", "Left atrial appendage closure device") +#define CODE_DCM_OstiumOfLeftAuricularAppendage DSRBasicCodedEntry("131120", "DCM", "Ostium of Left Auricular Appendage") +#define CODE_DCM_TransseptalPunctureHeight DSRBasicCodedEntry("131130", "DCM", "Transseptal puncture height") +#define CODE_DCM_AortaSinotubularJunctionArea DSRBasicCodedEntry("131140", "DCM", "Aorta sinotubular junction area") +#define CODE_DCM_AorticAnnulusCalcificationSeverity DSRBasicCodedEntry("131142", "DCM", "Aortic annulus calcification severity") +#define CODE_DCM_AorticAnnulusMaxDiameter DSRBasicCodedEntry("131143", "DCM", "Aortic annulus max diameter") +#define CODE_DCM_AorticAnnulusMinDiameter DSRBasicCodedEntry("131144", "DCM", "Aortic annulus min diameter") +#define CODE_DCM_AorticAnnulusPerimeter DSRBasicCodedEntry("131145", "DCM", "Aortic annulus perimeter") +#define CODE_DCM_AorticCalcificationVolume DSRBasicCodedEntry("131146", "DCM", "Aortic calcification volume") +#define CODE_DCM_AorticCommissuresCalcificationSeverity DSRBasicCodedEntry("131147", "DCM", "Aortic commissures calcification severity") +#define CODE_DCM_AorticRootHeight DSRBasicCodedEntry("131148", "DCM", "Aortic root height") +#define CODE_DCM_AorticSinotubularJunctionDiameter DSRBasicCodedEntry("131149", "DCM", "Aortic sinotubular junction diameter") +#define CODE_DCM_AorticSinusOfValsalvaArea DSRBasicCodedEntry("131150", "DCM", "Aortic sinus of valsalva area") +#define CODE_DCM_AorticValveCoaptationLength DSRBasicCodedEntry("131152", "DCM", "Aortic valve coaptation length") +#define CODE_DCM_AorticValveNoncoronaryLeafletIntercommissuralAngle DSRBasicCodedEntry("131153", "DCM", "Aortic valve noncoronary leaflet intercommissural angle") +#define CODE_DCM_AorticValveRightLeafletIntercommissuralAngle DSRBasicCodedEntry("131154", "DCM", "Aortic valve right leaflet intercommissural angle") +#define CODE_DCM_AorticValveLeftLeafletIntercommissuralAngle DSRBasicCodedEntry("131155", "DCM", "Aortic valve left leaflet intercommissural angle") +#define CODE_DCM_AorticValveNoncoronaryLeafletIntercommissuralDistance DSRBasicCodedEntry("131156", "DCM", "Aortic valve noncoronary leaflet intercommissural distance") +#define CODE_DCM_AorticValveRightLeafletIntercommissuralDistance DSRBasicCodedEntry("131157", "DCM", "Aortic valve right leaflet intercommissural distance") +#define CODE_DCM_AorticValveLeftLeafletIntercommissuralDistance DSRBasicCodedEntry("131158", "DCM", "Aortic valve left leaflet intercommissural distance") +#define CODE_DCM_AorticValveLeftCoronaryLeafletHeight DSRBasicCodedEntry("131159", "DCM", "Aortic valve left coronary leaflet height") +#define CODE_DCM_AorticValveLeftCoronaryLeafletLength DSRBasicCodedEntry("131160", "DCM", "Aortic valve left coronary leaflet length") +#define CODE_DCM_AorticValveNoncoronaryLeafletHeight DSRBasicCodedEntry("131161", "DCM", "Aortic valve noncoronary leaflet height") +#define CODE_DCM_AorticValveNoncoronaryLeafletLength DSRBasicCodedEntry("131162", "DCM", "Aortic valve noncoronary leaflet length") +#define CODE_DCM_AorticValveRightCoronaryLeafletHeight DSRBasicCodedEntry("131163", "DCM", "Aortic valve right coronary leaflet height") +#define CODE_DCM_AorticValveRightCoronaryLeafletLength DSRBasicCodedEntry("131164", "DCM", "Aortic valve right coronary leaflet length") +#define CODE_DCM_AscendingAortaDiameter DSRBasicCodedEntry("131165", "DCM", "Ascending Aorta diameter") +#define CODE_DCM_LeftMainCoronaryOstiumHeight DSRBasicCodedEntry("131166", "DCM", "Left main coronary ostium height") +#define CODE_DCM_LeftVentricularOutflowTractCalcificationSeverity DSRBasicCodedEntry("131167", "DCM", "Left ventricular outflow tract calcification severity") +#define CODE_DCM_MaximumAorticPlaqueThickness DSRBasicCodedEntry("131168", "DCM", "Maximum aortic plaque thickness") +#define CODE_DCM_RightCoronaryArteryOstiumHeight DSRBasicCodedEntry("131169", "DCM", "Right coronary artery ostium height") +#define CODE_DCM_RightVentricleDiastolicMajorAxis DSRBasicCodedEntry("131170", "DCM", "Right ventricle diastolic major axis") +#define CODE_DCM_RightVentricularDiastolicMidSegmentMinorAxis DSRBasicCodedEntry("131171", "DCM", "Right ventricular diastolic mid segment minor axis") +#define CODE_DCM_RightVentricularDiastolicBasalMinorAxis DSRBasicCodedEntry("131172", "DCM", "Right ventricular diastolic basal minor axis") +#define CODE_DCM_MitralAnteriorLeafletA1ScallopLength DSRBasicCodedEntry("131173", "DCM", "Mitral anterior leaflet A1 scallop length") +#define CODE_DCM_MitralAnteriorLeafletA2ScallopLength DSRBasicCodedEntry("131174", "DCM", "Mitral anterior leaflet A2 scallop length") +#define CODE_DCM_MitralAnteriorLeafletA3ScallopLength DSRBasicCodedEntry("131175", "DCM", "Mitral anterior leaflet A3 scallop length") +#define CODE_DCM_MitralAnteriorLeafletArea DSRBasicCodedEntry("131176", "DCM", "Mitral anterior leaflet area") +#define CODE_DCM_AortoMitralInterAnnularAngle DSRBasicCodedEntry("131177", "DCM", "Aorto-mitral inter annular angle") +#define CODE_DCM_MitralCommissureDistance DSRBasicCodedEntry("131178", "DCM", "Mitral commissure distance") +#define CODE_DCM_MitralTrigoneToTrigoneDistance DSRBasicCodedEntry("131179", "DCM", "Mitral trigone-to-trigone distance") +#define CODE_DCM_MitralAnnularExcursion DSRBasicCodedEntry("131180", "DCM", "Mitral annular excursion") +#define CODE_DCM_MitralAnnulusAnterolateralToPosteromedialDiameter DSRBasicCodedEntry("131181", "DCM", "Mitral annulus anterolateral to posteromedial diameter") +#define CODE_DCM_MitralAnnulusAnteroposteriorDiameter DSRBasicCodedEntry("131182", "DCM", "Mitral annulus anteroposterior diameter") +#define CODE_DCM_MitralAnnulusArea DSRBasicCodedEntry("131183", "DCM", "Mitral annulus area") +#define CODE_DCM_MitralAnnulusCalcificationSeverity DSRBasicCodedEntry("131184", "DCM", "Mitral annulus calcification severity") +#define CODE_DCM_MitralAnnulusCommissuralDiameter DSRBasicCodedEntry("131185", "DCM", "Mitral annulus commissural diameter") +#define CODE_DCM_MitralAnnulusDiameterRatio DSRBasicCodedEntry("131186", "DCM", "Mitral annulus diameter ratio") +#define CODE_DCM_MitralAnnulusHeight DSRBasicCodedEntry("131187", "DCM", "Mitral annulus height") +#define CODE_DCM_MitralAnnulusNonplanarityAngle DSRBasicCodedEntry("131188", "DCM", "Mitral annulus nonplanarity angle") +#define CODE_DCM_MitralAnnulusPerimeter DSRBasicCodedEntry("131189", "DCM", "Mitral annulus perimeter") +#define CODE_DCM_MitralValveCoaptationLength DSRBasicCodedEntry("131190", "DCM", "Mitral valve coaptation length") +#define CODE_DCM_MitralValveInterpapillaryDistance DSRBasicCodedEntry("131191", "DCM", "Mitral valve interpapillary distance") +#define CODE_DCM_AnterolateralPapillaryMuscleToTheLeftTrigone DSRBasicCodedEntry("131192", "DCM", "Anterolateral papillary muscle to the left trigone") +#define CODE_DCM_PosteromedialPapillaryMuscleToTheRightTrigone DSRBasicCodedEntry("131193", "DCM", "Posteromedial papillary muscle to the right trigone") +#define CODE_DCM_MitralValveProlapseArea DSRBasicCodedEntry("131194", "DCM", "Mitral valve prolapse area") +#define CODE_DCM_MitralValveProlapseVolume DSRBasicCodedEntry("131195", "DCM", "Mitral valve prolapse volume") +#define CODE_DCM_MitralValveSegmentFlailGap DSRBasicCodedEntry("131196", "DCM", "Mitral valve segment flail gap") +#define CODE_DCM_MitralValveSphericityIndex DSRBasicCodedEntry("131197", "DCM", "Mitral valve sphericity index") +#define CODE_DCM_MitralValveTentingHeight DSRBasicCodedEntry("131198", "DCM", "Mitral valve tenting height") +#define CODE_DCM_MitralValveTentingArea DSRBasicCodedEntry("131199", "DCM", "Mitral valve tenting area") +#define CODE_DCM_MitralValveTentingSegmentHeightA1P1 DSRBasicCodedEntry("131200", "DCM", "Mitral valve tenting segment height A1-P1") +#define CODE_DCM_MitralValveTentingSegmentHeightA2P2 DSRBasicCodedEntry("131201", "DCM", "Mitral valve tenting segment height A2-P2") +#define CODE_DCM_MitralValveTentingSegmentHeightA3P3 DSRBasicCodedEntry("131202", "DCM", "Mitral valve tenting segment height A3-P3") +#define CODE_DCM_PosteriorMitralValveLeafletArea DSRBasicCodedEntry("131203", "DCM", "Posterior mitral valve leaflet area") +#define CODE_DCM_PosteriorMitralValveLeafletLength DSRBasicCodedEntry("131204", "DCM", "Posterior mitral valve leaflet length") +#define CODE_DCM_PosteriorMitralValveP1LeafletScallopLength DSRBasicCodedEntry("131205", "DCM", "Posterior mitral valve P1 leaflet scallop length") +#define CODE_DCM_PosteriorMitralValveP2LeafletScallopLength DSRBasicCodedEntry("131206", "DCM", "Posterior mitral valve P2 leaflet scallop length") +#define CODE_DCM_PosteriorMitralValveP3LeafletScallopLength DSRBasicCodedEntry("131207", "DCM", "Posterior mitral valve P3 leaflet scallop length") +#define CODE_DCM_TricuspidAnnulusArea DSRBasicCodedEntry("131208", "DCM", "Tricuspid annulus area") +#define CODE_DCM_TricuspidAnnulusAreaDiastolicSystolicRatio DSRBasicCodedEntry("131209", "DCM", "Tricuspid annulus area diastolic systolic ratio") +#define CODE_DCM_TricuspidAnnulusPerimeter DSRBasicCodedEntry("131210", "DCM", "Tricuspid annulus perimeter") +#define CODE_DCM_TricuspidValveCoaptationLength DSRBasicCodedEntry("131211", "DCM", "Tricuspid valve coaptation length") +#define CODE_DCM_TricuspidValveMajorAxisDiastole DSRBasicCodedEntry("131212", "DCM", "Tricuspid valve major axis diastole") +#define CODE_DCM_TricuspidValveMinorAxis DSRBasicCodedEntry("131213", "DCM", "Tricuspid valve minor axis") +#define CODE_DCM_TricuspidValveSphericityIndex DSRBasicCodedEntry("131214", "DCM", "Tricuspid valve sphericity index") +#define CODE_DCM_TricuspidValveTentingHeight DSRBasicCodedEntry("131215", "DCM", "Tricuspid valve tenting height") +#define CODE_DCM_TricuspidValveTentingVolume DSRBasicCodedEntry("131216", "DCM", "Tricuspid valve tenting volume") +#define CODE_DCM_LeftAtrialAppendageClosureDeviceCircumference DSRBasicCodedEntry("131217", "DCM", "Left atrial appendage closure device circumference") +#define CODE_DCM_LeftAtrialAppendageClosureDeviceCompressionRatio DSRBasicCodedEntry("131218", "DCM", "Left atrial appendage closure device compression ratio") +#define CODE_DCM_LeftAtrialAppendageClosureDeviceDiameter DSRBasicCodedEntry("131219", "DCM", "Left atrial appendage closure device diameter") +#define CODE_DCM_LeftAtrialAppendageClosureDeviceSize DSRBasicCodedEntry("131220", "DCM", "Left atrial appendage closure device size") +#define CODE_DCM_LeftAtrialAppendageDepth DSRBasicCodedEntry("131221", "DCM", "Left atrial appendage depth") +#define CODE_DCM_LeftAtrialAppendageLandingZoneMajorAxis DSRBasicCodedEntry("131222", "DCM", "Left atrial appendage landing zone major axis") +#define CODE_DCM_LeftAtrialAppendageMajorAxis DSRBasicCodedEntry("131223", "DCM", "Left atrial appendage major axis") +#define CODE_DCM_LeftAtrialAppendageMinorAxis DSRBasicCodedEntry("131224", "DCM", "Left atrial appendage minor axis") +#define CODE_DCM_LeftAtrialAppendageOstiumPerimeter DSRBasicCodedEntry("131225", "DCM", "Left atrial appendage ostium perimeter") +#define CODE_DCM_FemaleTypical DSRBasicCodedEntry("131230", "DCM", "Female-typical") +#define CODE_DCM_MaleTypical DSRBasicCodedEntry("131231", "DCM", "Male-typical") +#define CODE_DCM_Specified DSRBasicCodedEntry("131232", "DCM", "Specified") +#define CODE_DCM_SubjectSexParametersForClinicalUseCategory DSRBasicCodedEntry("131233", "DCM", "Subject Sex Parameters for Clinical Use Category") +#define CODE_DCM_SexParametersForClinicalUseCategoryComment DSRBasicCodedEntry("131234", "DCM", "Sex Parameters for Clinical Use Category Comment") +#define CODE_DCM_SexParametersForClinicalUseCategoryReference DSRBasicCodedEntry("131235", "DCM", "Sex Parameters for Clinical Use Category Reference") +#define CODE_DCM_VisualFieldKeyMeasurements DSRBasicCodedEntry("131240", "DCM", "Visual Field Key Measurements") +#define CODE_DCM_OpticDiscKeyMeasurements DSRBasicCodedEntry("131241", "DCM", "Optic Disc Key Measurements") +#define CODE_DCM_CircumpapillaryRetinalNerveFiberLayerKeyMeasurements DSRBasicCodedEntry("131242", "DCM", "Circumpapillary Retinal Nerve Fiber Layer Key Measurements") +#define CODE_DCM_MacularThicknessKeyMeasurements DSRBasicCodedEntry("131243", "DCM", "Macular Thickness Key Measurements") +#define CODE_DCM_GanglionCellLayerKeyMeasurements DSRBasicCodedEntry("131244", "DCM", "Ganglion Cell Layer Key Measurements") +#define CODE_DCM_EndothelialCellCountKeyMeasurements DSRBasicCodedEntry("131245", "DCM", "Endothelial Cell Count Key Measurements") +#define CODE_DCM_OphthalmicImageROIMeasurements DSRBasicCodedEntry("131246", "DCM", "Ophthalmic Image ROI Measurements") +#define CODE_DCM_RepositionedROIOrGrid DSRBasicCodedEntry("131247", "DCM", "Repositioned ROI or grid") +#define CODE_DCM_VisualFieldGlobalDeviationFromNormal DSRBasicCodedEntry("131248", "DCM", "Visual Field Global Deviation from Normal") +#define CODE_DCM_VisualFieldLocalizedDeviationFromNormal DSRBasicCodedEntry("131249", "DCM", "Visual Field Localized Deviation From Normal") +#define CODE_DCM_FixationFalsePositiveRatio DSRBasicCodedEntry("131250", "DCM", "Fixation false positive ratio") +#define CODE_DCM_FixationFalsePositivePercent DSRBasicCodedEntry("131251", "DCM", "Fixation false positive percent") +#define CODE_DCM_FixationFalseNegativeRatio DSRBasicCodedEntry("131252", "DCM", "Fixation false negative ratio") +#define CODE_DCM_FixationFalseNegativePercent DSRBasicCodedEntry("131253", "DCM", "Fixation false negative percent") +#define CODE_DCM_FixationLossesRatio DSRBasicCodedEntry("131254", "DCM", "Fixation losses ratio") +#define CODE_DCM_AverageMacularThickness DSRBasicCodedEntry("131255", "DCM", "Average macular thickness") +#define CODE_DCM_CupToDiscAreaRatio DSRBasicCodedEntry("131256", "DCM", "Cup to disc area ratio") +#define CODE_DCM_CupToDiscRatioVertical DSRBasicCodedEntry("131257", "DCM", "Cup to disc ratio vertical") +#define CODE_DCM_CupToDiscRatioHorizontal DSRBasicCodedEntry("131258", "DCM", "Cup to disc ratio horizontal") +#define CODE_DCM_NeuroretinalRimArea DSRBasicCodedEntry("131259", "DCM", "Neuroretinal rim area") +#define CODE_DCM_NeuroretinalRimWidth DSRBasicCodedEntry("131260", "DCM", "Neuroretinal rim width") +#define CODE_DCM_OpticCupArea DSRBasicCodedEntry("131261", "DCM", "Optic cup area") +#define CODE_DCM_OpticDiscArea DSRBasicCodedEntry("131262", "DCM", "Optic disc area") +#define CODE_DCM_OpticCupVolume DSRBasicCodedEntry("131263", "DCM", "Optic cup volume") +#define CODE_DCM_RNFLAverageThickness DSRBasicCodedEntry("131264", "DCM", "RNFL average thickness") +#define CODE_DCM_RNFLInferiorSectorThickness DSRBasicCodedEntry("131265", "DCM", "RNFL inferior sector thickness") +#define CODE_DCM_RNFLSuperiorSectorThickness DSRBasicCodedEntry("131266", "DCM", "RNFL superior sector thickness") +#define CODE_DCM_RNFLTemporalSectorThickness DSRBasicCodedEntry("131267", "DCM", "RNFL temporal sector thickness") +#define CODE_DCM_RNFLNasalSectorThickness DSRBasicCodedEntry("131268", "DCM", "RNFL nasal sector thickness") +#define CODE_DCM_RNFLNasalSuperiorSectorThickness DSRBasicCodedEntry("131269", "DCM", "RNFL nasal-superior sector thickness") +#define CODE_DCM_RNFLNasalInferiorSectorThickness DSRBasicCodedEntry("131270", "DCM", "RNFL nasal-inferior sector thickness") +#define CODE_DCM_RNFLTemporalInferiorSectorThickness DSRBasicCodedEntry("131271", "DCM", "RNFL temporal-inferior sector thickness") +#define CODE_DCM_RNFLTemporalSuperiorSectorThickness DSRBasicCodedEntry("131272", "DCM", "RNFL temporal-superior sector thickness") +#define CODE_DCM_RetinalNerveFiberLayerSymmetry DSRBasicCodedEntry("131273", "DCM", "Retinal nerve fiber layer symmetry") +#define CODE_DCM_RetinalROIWidth DSRBasicCodedEntry("131274", "DCM", "Retinal ROI width") +#define CODE_DCM_RetinalROIHeight DSRBasicCodedEntry("131275", "DCM", "Retinal ROI height") +#define CODE_DCM_RNFLClockfacePosition1Thickness DSRBasicCodedEntry("131276", "DCM", "RNFL clockface position 1 thickness") +#define CODE_DCM_RNFLClockfacePosition2Thickness DSRBasicCodedEntry("131277", "DCM", "RNFL clockface position 2 thickness") +#define CODE_DCM_RNFLClockfacePosition3Thickness DSRBasicCodedEntry("131278", "DCM", "RNFL clockface position 3 thickness") +#define CODE_DCM_RNFLClockfacePosition4Thickness DSRBasicCodedEntry("131279", "DCM", "RNFL clockface position 4 thickness") +#define CODE_DCM_RNFLClockfacePosition5Thickness DSRBasicCodedEntry("131280", "DCM", "RNFL clockface position 5 thickness") +#define CODE_DCM_RNFLClockfacePosition6Thickness DSRBasicCodedEntry("131281", "DCM", "RNFL clockface position 6 thickness") +#define CODE_DCM_RNFLClockfacePosition7Thickness DSRBasicCodedEntry("131282", "DCM", "RNFL clockface position 7 thickness") +#define CODE_DCM_RNFLClockfacePosition8Thickness DSRBasicCodedEntry("131283", "DCM", "RNFL clockface position 8 thickness") +#define CODE_DCM_RNFLClockfacePosition9Thickness DSRBasicCodedEntry("131284", "DCM", "RNFL clockface position 9 thickness") +#define CODE_DCM_RNFLClockfacePosition10Thickness DSRBasicCodedEntry("131285", "DCM", "RNFL clockface position 10 thickness") +#define CODE_DCM_RNFLClockfacePosition11Thickness DSRBasicCodedEntry("131286", "DCM", "RNFL clockface position 11 thickness") +#define CODE_DCM_RNFLClockfacePosition12Thickness DSRBasicCodedEntry("131287", "DCM", "RNFL clockface position 12 thickness") +#define CODE_DCM_AverageGanglionCellThickness DSRBasicCodedEntry("131288", "DCM", "Average ganglion cell thickness") +#define CODE_DCM_MinimumGanglionCellThickness DSRBasicCodedEntry("131289", "DCM", "Minimum ganglion cell thickness") +#define CODE_DCM_AverageGanglionCellThicknessSuperiorSector DSRBasicCodedEntry("131290", "DCM", "Average ganglion cell thickness superior sector") +#define CODE_DCM_AverageGanglionCellThicknessNasalSuperiorSector DSRBasicCodedEntry("131291", "DCM", "Average ganglion cell thickness nasal-superior sector") +#define CODE_DCM_AverageGanglionCellThicknessNasalSector DSRBasicCodedEntry("131292", "DCM", "Average ganglion cell thickness nasal sector") +#define CODE_DCM_AverageGanglionCellThicknessNasalInferiorSector DSRBasicCodedEntry("131293", "DCM", "Average ganglion cell thickness nasal-inferior sector") +#define CODE_DCM_AverageGanglionCellThicknessInferiorSector DSRBasicCodedEntry("131294", "DCM", "Average ganglion cell thickness inferior sector") +#define CODE_DCM_AverageGanglionCellThicknessTemporalInferiorSector DSRBasicCodedEntry("131295", "DCM", "Average ganglion cell thickness temporal-inferior sector") +#define CODE_DCM_AverageGanglionCellThicknessTemporalSector DSRBasicCodedEntry("131296", "DCM", "Average ganglion cell thickness temporal sector") +#define CODE_DCM_AverageGanglionCellThicknessTemporalSuperiorSector DSRBasicCodedEntry("131297", "DCM", "Average ganglion cell thickness temporal-superior sector") +#define CODE_DCM_AverageGanglionCellThicknessInPosteriorPoleGrid DSRBasicCodedEntry("131298", "DCM", "Average ganglion cell thickness in posterior pole grid") +#define CODE_DCM_GanglionCellAndInnerPlexiformLayers DSRBasicCodedEntry("131299", "DCM", "Ganglion cell and inner plexiform layers") +#define CODE_DCM_GanglionCellComplex DSRBasicCodedEntry("131300", "DCM", "Ganglion cell complex") +#define CODE_DCM_SemicircularSectors DSRBasicCodedEntry("131301", "DCM", "Semicircular sectors") +#define CODE_DCM_QuadrantSectors DSRBasicCodedEntry("131302", "DCM", "Quadrant sectors") +#define CODE_DCM_SNITRectangularSectors DSRBasicCodedEntry("131303", "DCM", "SNIT rectangular sectors") +#define CODE_DCM_EllipticalAnnulusSectors DSRBasicCodedEntry("131304", "DCM", "Elliptical annulus sectors") +#define CODE_DCM_GarwayHeathSectors DSRBasicCodedEntry("131305", "DCM", "Garway-Heath sectors") +#define CODE_DCM_QuadrantOctantSectors DSRBasicCodedEntry("131306", "DCM", "Quadrant-octant sectors") +#define CODE_DCM_PosteriorPole8x8Grid DSRBasicCodedEntry("131307", "DCM", "Posterior pole 8x8 grid") +#define CODE_DCM_RNFLClockfaceMethod DSRBasicCodedEntry("131308", "DCM", "RNFL Clockface Method") +#define CODE_DCM_EndothelialCellDensity DSRBasicCodedEntry("131309", "DCM", "Endothelial cell density") +#define CODE_DCM_GeographicAtrophyArea DSRBasicCodedEntry("131310", "DCM", "Geographic atrophy area") +#define CODE_DCM_PlanMeetsPrescription DSRBasicCodedEntry("131315", "DCM", "Plan meets prescription") +#define CODE_DCM_PlanQAPassed DSRBasicCodedEntry("131316", "DCM", "Plan QA passed") +#define CODE_DCM_LinearBoltzmannTransportEquation DSRBasicCodedEntry("131320", "DCM", "Linear Boltzmann Transport Equation") +#define CODE_DCM_PencilBeamConvolution DSRBasicCodedEntry("131321", "DCM", "Pencil Beam Convolution") +#define CODE_DCM_TMRAndOARRatios DSRBasicCodedEntry("131322", "DCM", "TMR and OAR Ratios") +#define CODE_DCM_AAPMTG43 DSRBasicCodedEntry("131323", "DCM", "AAPM TG-43") +#define CODE_DCM_ConvolutionSuperposition DSRBasicCodedEntry("131324", "DCM", "Convolution Superposition") +#define CODE_DCM_NumberOfHistories DSRBasicCodedEntry("131325", "DCM", "Number of Histories") +#define CODE_DCM_AcceptableUncertaintyInDoseResult DSRBasicCodedEntry("131326", "DCM", "Acceptable Uncertainty in Dose Result") +#define CODE_DCM_CalibrationIntendedDoseIndex DSRBasicCodedEntry("131330", "DCM", "Calibration Intended Dose Index") +#define CODE_DCM_CalibrationIntendedAcquisitionProtocol DSRBasicCodedEntry("131331", "DCM", "Calibration Intended Acquisition Protocol") +#define CODE_DCM_UltrasoundAttenuationImaging DSRBasicCodedEntry("131340", "DCM", "Ultrasound Attenuation Imaging") +#define CODE_DCM_UltrasoundAttenuationCoefficient DSRBasicCodedEntry("131341", "DCM", "Ultrasound Attenuation Coefficient") +#define CODE_DCM_MeanUltrasoundAttenuationCoefficient DSRBasicCodedEntry("131342", "DCM", "Mean Ultrasound Attenuation Coefficient") +#define CODE_DCM_MedianUltrasoundAttenuationCoefficient DSRBasicCodedEntry("131343", "DCM", "Median Ultrasound Attenuation Coefficient") +#define CODE_DCM_StandardDeviationOfUltrasoundAttenuationCoefficient DSRBasicCodedEntry("131344", "DCM", "Standard Deviation of Ultrasound Attenuation Coefficient") +#define CODE_DCM_InterquartileRangeOfUltrasoundAttenuationCoefficient DSRBasicCodedEntry("131345", "DCM", "Interquartile Range of Ultrasound Attenuation Coefficient") +#define CODE_DCM_InterquartileRangeToMedianRatioOfUltrasoundAttenCoeff DSRBasicCodedEntry("131346", "DCM", "Interquartile Range to Median Ratio of Ultrasound Atten Coeff") +#define CODE_DCM_CardiacAxisAngle DSRBasicCodedEntry("131350", "DCM", "Cardiac Axis Angle") +#define CODE_DCM_DVSWavePeakVelocity DSRBasicCodedEntry("131351", "DCM", "DV S-wave peak velocity") +#define CODE_DCM_DVAWavePeakVelocity DSRBasicCodedEntry("131352", "DCM", "DV a-wave peak velocity") +#define CODE_DCM_DVPreloadIndex DSRBasicCodedEntry("131353", "DCM", "DV preload index") +#define CODE_DCM_DVSA DSRBasicCodedEntry("131354", "DCM", "DV S/a") +#define CODE_DCM_RejectionWithdrawn DSRBasicCodedEntry("131360", "DCM", "Rejection Withdrawn") +#define CODE_DCM_FetalAnatomySurvey DSRBasicCodedEntry("131370", "DCM", "Fetal Anatomy Survey") +#define CODE_DCM_AbdominalAttachmentOfUmbilicalCord DSRBasicCodedEntry("131371", "DCM", "Abdominal attachment of umbilical cord") +#define CODE_DCM_RetronasalTriangle DSRBasicCodedEntry("131372", "DCM", "Retronasal triangle") +#define CODE_DCM_MidsagittalFacialProfile DSRBasicCodedEntry("131373", "DCM", "Midsagittal facial profile") +#define CODE_DCM_CardiacAxis DSRBasicCodedEntry("131374", "DCM", "Cardiac axis") +#define CODE_DCM_JugularLymphaticSac DSRBasicCodedEntry("131375", "DCM", "Jugular lymphatic sac") +#define CODE_DCM_TransthalamicCoronalView DSRBasicCodedEntry("131376", "DCM", "Transthalamic coronal view") +#define CODE_DCM_MidsagittalView DSRBasicCodedEntry("131377", "DCM", "Midsagittal view") +#define CODE_DCM_HighShortAxisView DSRBasicCodedEntry("131378", "DCM", "High short axis view") +#define CODE_DCM_AntegradeDuctusVenosus DSRBasicCodedEntry("131379", "DCM", "Antegrade ductus venosus") +#define CODE_DCM_ISUOG1stTrimester2023 DSRBasicCodedEntry("131380", "DCM", "ISUOG 1st Trimester 2023") +#define CODE_DCM_ISUOG2ndTrimester2022 DSRBasicCodedEntry("131381", "DCM", "ISUOG 2nd Trimester 2022") +#define CODE_DCM_ISUOG3rdTrimester2024 DSRBasicCodedEntry("131382", "DCM", "ISUOG 3rd Trimester 2024") +#define CODE_DCM_JSUMFetalMorphology2021 DSRBasicCodedEntry("131383", "DCM", "JSUM Fetal Morphology 2021") +#define CODE_DCM_JDMSFetalAnatomy2014 DSRBasicCodedEntry("131384", "DCM", "JDMS Fetal Anatomy 2014") #endif diff --git a/dcmsr/include/dcmtk/dcmsr/codes/ncit.h b/dcmsr/include/dcmtk/dcmsr/codes/ncit.h index c6260009..a429e757 100644 --- a/dcmsr/include/dcmtk/dcmsr/codes/ncit.h +++ b/dcmsr/include/dcmtk/dcmsr/codes/ncit.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file with NCI Thesaurus Code Definitions (Coding Scheme "NCIt") * - * Generated automatically from DICOM PS 3.16-2024e - * File created on 2024-11-16 10:17:31 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 11:52:23 by J. Riesmeier * */ @@ -34,7 +34,7 @@ * code definitions * *--------------------*/ -// total number of codes: 51 +// total number of codes: 61 // - retired: 0 // - no name: 0 // - not unique: 0 @@ -57,8 +57,10 @@ #define CODE_NCIt_Sarcosine_C11 DSRBasicCodedEntry("C122684", "NCIt", "Sarcosine C^11^") #define CODE_NCIt_IrreversibleElectroporation DSRBasicCodedEntry("C131483", "NCIt", "Irreversible electroporation") #define CODE_NCIt_4Kscore DSRBasicCodedEntry("C142184", "NCIt", "4Kscore") +#define CODE_NCIt_Pembrolizumab_89Zr DSRBasicCodedEntry("C148167", "NCIt", "Pembrolizumab ^89^Zr") #define CODE_NCIt_SoftTissueSarcoma_excludingRhabdomyosarcoma DSRBasicCodedEntry("C148457", "NCIt", "Soft tissue sarcoma, excluding rhabdomyosarcoma") #define CODE_NCIt_Cryoablation DSRBasicCodedEntry("C15215", "NCIt", "Cryoablation") +#define CODE_NCIt_PAXgeneTissueSystem DSRBasicCodedEntry("C185113", "NCIt", "PAXgene Tissue System") #define CODE_NCIt_166Holmium DSRBasicCodedEntry("C1943", "NCIt", "^166^Holmium") #define CODE_NCIt_MouseMammaryFatPad DSRBasicCodedEntry("C22550", "NCIt", "Mouse mammary fat pad") #define CODE_NCIt_Middle DSRBasicCodedEntry("C25569", "NCIt", "Middle") @@ -78,11 +80,18 @@ #define CODE_NCIt_Experience DSRBasicCodedEntry("C54627", "NCIt", "Experience") #define CODE_NCIt_Reviewer DSRBasicCodedEntry("C54634", "NCIt", "Reviewer") #define CODE_NCIt_Ion DSRBasicCodedEntry("C597", "NCIt", "Ion") +#define CODE_NCIt_FluoroazomycinArabinoside_F18 DSRBasicCodedEntry("C62520", "NCIt", "Fluoroazomycin arabinoside F^18^") #define CODE_NCIt_AdLibitum DSRBasicCodedEntry("C64636", "NCIt", "ad libitum") #define CODE_NCIt_ActivitySession DSRBasicCodedEntry("C67447", "NCIt", "Activity Session") #define CODE_NCIt_FocusedUltrasoundAblation DSRBasicCodedEntry("C68681", "NCIt", "Focused ultrasound ablation") #define CODE_NCIt_UnitConversionFactor DSRBasicCodedEntry("C70774", "NCIt", "Unit Conversion Factor") #define CODE_NCIt_MedicalProductExpirationDate DSRBasicCodedEntry("C70854", "NCIt", "Medical Product Expiration Date") +#define CODE_NCIt_FitzpatrickSkinTypeI DSRBasicCodedEntry("C74569", "NCIt", "Fitzpatrick Skin Type I") +#define CODE_NCIt_FitzpatrickSkinTypeII DSRBasicCodedEntry("C74570", "NCIt", "Fitzpatrick Skin Type II") +#define CODE_NCIt_FitzpatrickSkinTypeIII DSRBasicCodedEntry("C74571", "NCIt", "Fitzpatrick Skin Type III") +#define CODE_NCIt_FitzpatrickSkinTypeIV DSRBasicCodedEntry("C74572", "NCIt", "Fitzpatrick Skin Type IV") +#define CODE_NCIt_FitzpatrickSkinTypeV DSRBasicCodedEntry("C74573", "NCIt", "Fitzpatrick Skin Type V") +#define CODE_NCIt_FitzpatrickSkinTypeVI DSRBasicCodedEntry("C74574", "NCIt", "Fitzpatrick Skin Type VI") #define CODE_NCIt_CardiotonicAgent DSRBasicCodedEntry("C78322", "NCIt", "Cardiotonic agent") #define CODE_NCIt_NonEnhancingLesion DSRBasicCodedEntry("C81175", "NCIt", "Non-Enhancing Lesion") #define CODE_NCIt_ImagingRegionOfInterest DSRBasicCodedEntry("C85402", "NCIt", "Imaging Region of Interest") @@ -93,6 +102,7 @@ #define CODE_NCIt_HousingHumidity DSRBasicCodedEntry("C90395", "NCIt", "Housing humidity") #define CODE_NCIt_LightCycle DSRBasicCodedEntry("C90419", "NCIt", "Light cycle") #define CODE_NCIt_WaterDelivery DSRBasicCodedEntry("C90486", "NCIt", "Water delivery") +#define CODE_NCIt_2Thymidine_C11 DSRBasicCodedEntry("C90936", "NCIt", "2-Thymidine C^11^") #define CODE_NCIt_Dosimetrist DSRBasicCodedEntry("C93176", "NCIt", "Dosimetrist") #define CODE_NCIt_Abnormality DSRBasicCodedEntry("C9440", "NCIt", "Abnormality") #define CODE_NCIt_ReferenceRegion DSRBasicCodedEntry("C94970", "NCIt", "Reference Region") diff --git a/dcmsr/include/dcmtk/dcmsr/codes/umls.h b/dcmsr/include/dcmtk/dcmsr/codes/umls.h index 49b184db..2e8cd36a 100644 --- a/dcmsr/include/dcmtk/dcmsr/codes/umls.h +++ b/dcmsr/include/dcmtk/dcmsr/codes/umls.h @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Header file with UMLS Code Definitions (Coding Scheme "UMLS") * - * Generated automatically from DICOM PS 3.16-2024e - * File created on 2024-11-16 10:17:31 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 11:52:22 by J. Riesmeier * */ @@ -34,7 +34,7 @@ * code definitions * *--------------------*/ -// total number of codes: 51 +// total number of codes: 60 // - retired: 0 // - no name: 0 // - not unique: 0 @@ -68,6 +68,8 @@ #define CODE_UMLS_Duration DSRBasicCodedEntry("C0449238", "UMLS", "Duration") #define CODE_UMLS_InfantOfMotherWithGestationalDiabetes DSRBasicCodedEntry("C0456029", "UMLS", "Infant of mother with gestational diabetes") #define CODE_UMLS_CoefficientOfVariance DSRBasicCodedEntry("C0681921", "UMLS", "Coefficient of Variance") +#define CODE_UMLS_HeartSize DSRBasicCodedEntry("C0744689", "UMLS", "Heart size") +#define CODE_UMLS_PercutaneousClosureOfAtrialSeptalDefect DSRBasicCodedEntry("C0844084", "UMLS", "Percutaneous closure of atrial septal defect") #define CODE_UMLS_ManufacturerName DSRBasicCodedEntry("C0947322", "UMLS", "Manufacturer Name") #define CODE_UMLS_Intern DSRBasicCodedEntry("C1144859", "UMLS", "Intern") #define CODE_UMLS_ConsultingPhysician DSRBasicCodedEntry("C1441532", "UMLS", "Consulting Physician") @@ -82,22 +84,29 @@ #define CODE_UMLS_Referring DSRBasicCodedEntry("C1709880", "UMLS", "Referring") #define CODE_UMLS_Variance DSRBasicCodedEntry("C1711260", "UMLS", "Variance") #define CODE_UMLS_Sonographer DSRBasicCodedEntry("C1954848", "UMLS", "Sonographer") +#define CODE_UMLS_AorticSinusOfValsalvaDiameter DSRBasicCodedEntry("C2059455", "UMLS", "Aortic sinus of valsalva diameter") +#define CODE_UMLS_AorticAnnulusArea DSRBasicCodedEntry("C2059685", "UMLS", "Aortic annulus area") #define CODE_UMLS_MobileSkinLesion DSRBasicCodedEntry("C2071496", "UMLS", "Mobile skin lesion") #define CODE_UMLS_Neuroradiology DSRBasicCodedEntry("C2183225", "UMLS", "Neuroradiology") #define CODE_UMLS_InverseRatioVentilation DSRBasicCodedEntry("C2223982", "UMLS", "Inverse ratio ventilation") #define CODE_UMLS_RootMeanSquare DSRBasicCodedEntry("C2347976", "UMLS", "Root Mean Square") #define CODE_UMLS_TimePoint DSRBasicCodedEntry("C2348792", "UMLS", "Time Point") #define CODE_UMLS_Edotreotide_Ga68 DSRBasicCodedEntry("C2713594", "UMLS", "Edotreotide Ga^68^") +#define CODE_UMLS_ImplantationOfMitralValveLeafletClip DSRBasicCodedEntry("C2921037", "UMLS", "Implantation of mitral valve leaflet clip") #define CODE_UMLS_FluoropropylDihydrotetrabenazine_F18 DSRBasicCodedEntry("C2934038", "UMLS", "Fluoropropyl-dihydrotetrabenazine F^18^") #define CODE_UMLS_ISO1_F18 DSRBasicCodedEntry("C2981788", "UMLS", "ISO-1 F^18^") #define CODE_UMLS_RadiationPhysicist DSRBasicCodedEntry("C2985483", "UMLS", "Radiation Physicist") +#define CODE_UMLS_LeftAtrialAppendageOcclusion DSRBasicCodedEntry("C3275093", "UMLS", "Left atrial appendage occlusion") +#define CODE_UMLS_IntoleranceToAnticoagulation DSRBasicCodedEntry("C3468959", "UMLS", "Intolerance to anticoagulation") #define CODE_UMLS_Pretreatment DSRBasicCodedEntry("C3539075", "UMLS", "Pretreatment") #define CODE_UMLS_DistalPhalanx DSRBasicCodedEntry("C3669027", "UMLS", "Distal phalanx") #define CODE_UMLS_RoomAir DSRBasicCodedEntry("C3846005", "UMLS", "Room air") +#define CODE_UMLS_LeftAtrialDilation DSRBasicCodedEntry("C4015487", "UMLS", "Left atrial dilation") #define CODE_UMLS_THK5351_F18 DSRBasicCodedEntry("C4279748", "UMLS", "THK5351 F^18^") #define CODE_UMLS_MK6240_F18 DSRBasicCodedEntry("C4506764", "UMLS", "MK-6240 F^18^") #define CODE_UMLS_UCBJ_C11 DSRBasicCodedEntry("C4506788", "UMLS", "UCB-J C^11^") #define CODE_UMLS_THK5317_F18 DSRBasicCodedEntry("C4550127", "UMLS", "THK5317 F^18^") +#define CODE_UMLS_PI2620_F18 DSRBasicCodedEntry("C5433257", "UMLS", "PI-2620 F^18^") #define CODE_UMLS_MiddleEasternOrNorthAfrican DSRBasicCodedEntry("C5690844", "UMLS", "Middle Eastern or North African") #endif diff --git a/dcmsr/include/dcmtk/dcmsr/dsrcomvl.h b/dcmsr/include/dcmtk/dcmsr/dsrcomvl.h index 24512fc7..4df8b73e 100644 --- a/dcmsr/include/dcmtk/dcmsr/dsrcomvl.h +++ b/dcmsr/include/dcmtk/dcmsr/dsrcomvl.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -179,6 +179,10 @@ class DCMTK_DCMSR_EXPORT DSRCompositeReferenceValue * Value is increased automatically by 1 after a new entry has been added. * @param flags flag used to customize the output (see DSRTypes::HF_xxx) * @param urlPrefix optional URL prefix used for hyperlink to referenced composite object + ** @note Please note that using parameter 'urlPrefix' can lead to security issues, as an + * attacker could misuse it to potentially inject dangerous content into the HTML/XHTML + * output. The value of this parameter is not checked. This is also true for derived + * classes. ** @return status, EC_Normal if successful, an error code otherwise */ virtual OFCondition renderHTML(STD_NAMESPACE ostream &docStream, diff --git a/dcmsr/include/dcmtk/dcmsr/dsrctxgr.h b/dcmsr/include/dcmtk/dcmsr/dsrctxgr.h index a82c56c1..cf9fa05a 100644 --- a/dcmsr/include/dcmtk/dcmsr/dsrctxgr.h +++ b/dcmsr/include/dcmtk/dcmsr/dsrctxgr.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2016, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -86,6 +86,14 @@ class DCMTK_DCMSR_EXPORT DSRContextGroup return MappingResource; } + /** get context group keyword + ** @return keyword of the context group (might be empty) + */ + inline const OFString &getKeyword() const + { + return Keyword; + } + /** get context group version (optional) ** @return version of the context group (might be empty) */ @@ -203,6 +211,7 @@ class DCMTK_DCMSR_EXPORT DSRContextGroup /** constructor ** @param contextIdentifier identifier of the context group * @param mappingResource mapping resource that defines the context group + * @param contextGroupKeyword keyword of the context group (optional) * @param contextGroupVersion version of the context group (optional) * @param contextGroupUID unique identifier of the context group (optional) * @param selectedValue coded entry to be selected as the current value @@ -210,6 +219,7 @@ class DCMTK_DCMSR_EXPORT DSRContextGroup */ DSRContextGroup(const OFString &contextIdentifier, const OFString &mappingResource, + const OFString &contextGroupKeyword = "", const OFString &contextGroupVersion = "", const OFString &contextGroupUID = "", const DSRCodedEntryValue &selectedValue = DSRCodedEntryValue()); @@ -265,6 +275,8 @@ class DCMTK_DCMSR_EXPORT DSRContextGroup const OFString Identifier; /// mapping resource (VR=CS, mandatory) const OFString MappingResource; + /// context group keyword (optional) + const OFString Keyword; /// context group version (VR=DT, optional) const OFString Version; /// context group UID (VR=UI, optional) diff --git a/dcmsr/include/dcmtk/dcmsr/dsrdoc.h b/dcmsr/include/dcmtk/dcmsr/dsrdoc.h index b670f16b..5feeb411 100644 --- a/dcmsr/include/dcmtk/dcmsr/dsrdoc.h +++ b/dcmsr/include/dcmtk/dcmsr/dsrdoc.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -32,6 +32,7 @@ #include "dcmtk/dcmsr/dsrrefin.h" #include "dcmtk/dcmsr/dsrcsidl.h" +#include "dcmtk/dcmdata/dcvras.h" #include "dcmtk/dcmdata/dcvrcs.h" #include "dcmtk/dcmdata/dcvrda.h" #include "dcmtk/dcmdata/dcvrds.h" @@ -200,6 +201,10 @@ class DCMTK_DCMSR_EXPORT DSRDocument * objects. If NULL, the default URL prefix is used, which is * defined by DEFAULT_HTML_HYPERLINK_PREFIX_FOR_COMPOSITE_OBJECTS * (http://localhost/dicom.cgi). + ** @note Please note that using the parameter 'styleSheet' or 'urlPrefix' can lead to + * security issues, as an attacker could misuse them to potentially inject dangerous + * content into the HTML/XHTML output. The values passed to these parameters are not + * checked, neither the URL and prefix nor the content of the specified CSS file. ** @return status, EC_Normal if successful, an error code otherwise */ virtual OFCondition renderHTML(STD_NAMESPACE ostream &stream, @@ -532,6 +537,14 @@ class DCMTK_DCMSR_EXPORT DSRDocument virtual OFCondition getPatientSex(OFString &value, const signed long pos = 0) const; + /** get patient's age + ** @param value reference to variable in which the value should be stored + * @param pos index of the value to get (0..vm-1), -1 for all components + ** @return status, EC_Normal if successful, an error code otherwise + */ + virtual OFCondition getPatientAge(OFString &value, + const signed long pos = 0) const; + /** get patient's size ** @param value reference to variable in which the value should be stored * @param pos index of the value to get (0..vm-1), -1 for all components @@ -808,6 +821,14 @@ class DCMTK_DCMSR_EXPORT DSRDocument virtual OFCondition setPatientSex(const OFString &value, const OFBool check = OFTrue); + /** set patient's age + ** @param value value to be set (single value only) or "" for no value + * @param check check 'value' for conformance with VR (AS) and VM (1) if enabled + ** @return status, EC_Normal if successful, an error code otherwise + */ + virtual OFCondition setPatientAge(const OFString &value, + const OFBool check = OFTrue); + /** set patient's size ** @param value value to be set (single value only) or "" for no value * @param check check 'value' for conformance with VR (DS) and VM (1) if enabled @@ -1064,14 +1085,14 @@ class DCMTK_DCMSR_EXPORT DSRDocument /** create a new document. * A new SOP instance is only created if the current document type was valid/supported. - * Please note that the current document is deleted (cleared). + * Please note that the current document is deleted (cleared) by this method. ** @return status, EC_Normal if successful, an error code otherwise */ virtual OFCondition createNewDocument(); /** create a new document of the specified type. * A new SOP instance is only created if the current document type was valid/supported. - * Please note that the current document is deleted by this method. + * Please note that the current document is deleted (cleared) by this method. ** @param documentType type of the new SR document (see DSRTypes::E_DocumentType) ** @return status, EC_Normal if successful, an error code otherwise */ @@ -1408,6 +1429,8 @@ class DCMTK_DCMSR_EXPORT DSRDocument // --- Patient Study Module (U) --- + /// Patient's Age: (AS, 1, 3) + DcmAgeString PatientAge; /// Patient's Size: (DS, 1, 3) DcmDecimalString PatientSize; /// Patient's Weight: (DS, 1, 3) diff --git a/dcmsr/include/dcmtk/dcmsr/dsrdoctn.h b/dcmsr/include/dcmtk/dcmsr/dsrdoctn.h index a17919bc..a9f14c0d 100644 --- a/dcmsr/include/dcmtk/dcmsr/dsrdoctn.h +++ b/dcmsr/include/dcmtk/dcmsr/dsrdoctn.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -227,6 +227,10 @@ class DCMTK_DCMSR_EXPORT DSRDocumentTreeNode * @param flags flag used to customize the output (see DSRTypes::HF_xxx) * @param urlPrefix optional URL prefix used for hyperlinks to referenced composite * objects. If NULL, the default URL prefix is used. + ** @note Please note that using parameter 'urlPrefix' can lead to security issues, as an + * attacker could misuse it to potentially inject dangerous content into the HTML/XHTML + * output. The value of this parameter is not checked. This is also true for derived + * classes. ** @return status, EC_Normal if successful, an error code otherwise */ virtual OFCondition renderHTML(STD_NAMESPACE ostream &docStream, diff --git a/dcmsr/include/dcmtk/dcmsr/dsrdoctr.h b/dcmsr/include/dcmtk/dcmsr/dsrdoctr.h index 354bece5..e0ad77eb 100644 --- a/dcmsr/include/dcmtk/dcmsr/dsrdoctr.h +++ b/dcmsr/include/dcmtk/dcmsr/dsrdoctr.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -139,6 +139,9 @@ class DCMTK_DCMSR_EXPORT DSRDocumentTree * @param flags optional flag used to customize the output (see DSRTypes::HF_xxx) * @param urlPrefix optional URL prefix used for hyperlinks to referenced composite * objects. If NULL, the default URL prefix is used. + ** @note Please note that using parameter 'urlPrefix' can lead to security issues, as an + * attacker could misuse it to potentially inject dangerous content into the HTML/XHTML + * output. The value of this parameter is not checked. ** @return status, EC_Normal if successful, an error code otherwise */ virtual OFCondition renderHTML(STD_NAMESPACE ostream &docStream, diff --git a/dcmsr/include/dcmtk/dcmsr/dsrimgvl.h b/dcmsr/include/dcmtk/dcmsr/dsrimgvl.h index 8fcf4ed6..8f9873f1 100644 --- a/dcmsr/include/dcmtk/dcmsr/dsrimgvl.h +++ b/dcmsr/include/dcmtk/dcmsr/dsrimgvl.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -161,12 +161,19 @@ class DCMTK_DCMSR_EXPORT DSRImageReferenceValue */ virtual OFBool isSegmentation() const; + /** check whether an icon image is associated with this image reference. + * This method does not check whether the icon image is valid. + ** @return OFTrue if image reference has an icon image, OFFalse otherwise + */ + virtual OFBool hasIconImage() const; + /** print image reference. * The output of a typical image reference value looks like this: (CT image,"1.2.3") or * (CT image,"1.2.3"),(GSPS,"1.2.3.4") if a presentation state is present. * If the SOP class UID is unknown, the UID is printed instead of the related name. * Also, the list of referenced frame/segment numbers is shown, but not the two UIDs of - * the real world value mapping object (if referenced). + * the real world value mapping object (if referenced). The optional icon image is never + * shown. ** @param stream output stream to which the image reference value should be printed * @param flags flag used to customize the output (see DSRTypes::PF_xxx) ** @return status, EC_Normal if successful, an error code otherwise @@ -201,6 +208,10 @@ class DCMTK_DCMSR_EXPORT DSRImageReferenceValue * has been added. * @param flags flag used to customize the output (see DSRTypes::HF_xxx) * @param urlPrefix optional URL prefix used for hyperlink to referenced composite object + ** @note Please note that using parameter 'urlPrefix' can lead to security issues, as an + * attacker could misuse it to potentially inject dangerous content into the HTML/XHTML + * output. The value of this parameter is not checked. This is also true for derived + * classes. ** @return status, EC_Normal if successful, an error code otherwise */ virtual OFCondition renderHTML(STD_NAMESPACE ostream &docStream, @@ -504,6 +515,12 @@ class DCMTK_DCMSR_EXPORT DSRImageReferenceValue */ OFCondition checkCurrentValue(const OFBool reportWarnings = OFFalse) const; + /** copy the given icon image to replace the currently stored one. + * The currently stored icon image is always deleted first. + * @param image pointer to the icon image to be copied (if not NULL) + */ + void copyIconImage(DicomImage *image); + private: diff --git a/dcmsr/include/dcmtk/dcmsr/dsrtypes.h b/dcmsr/include/dcmtk/dcmsr/dsrtypes.h index ba1ecc9a..7d2f2771 100644 --- a/dcmsr/include/dcmtk/dcmsr/dsrtypes.h +++ b/dcmsr/include/dcmtk/dcmsr/dsrtypes.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -121,7 +121,7 @@ class DSRIODConstraintChecker; * These error codes can be used in addition to the general purpose * codes defined in module dcmdata. */ -//@{ +///@{ /// error: the document type (SOP class UID) is unknown or not supported extern DCMTK_DCMSR_EXPORT const OFConditionConst SR_EC_UnknownDocumentType; @@ -225,7 +225,7 @@ extern DCMTK_DCMSR_EXPORT const OFConditionConst SR_EC_InvalidTemplateStructure; /// error: cannot process document tree with included templates extern DCMTK_DCMSR_EXPORT const OFConditionConst SR_EC_CannotProcessIncludedTemplates; -//@} +///@} /*---------------------* @@ -248,7 +248,7 @@ class DCMTK_DCMSR_EXPORT DSRTypes * These flags can be combined and passed to the read() methods. * The 'shortcut' flags can be used for common combinations. */ - //@{ + ///@{ /// read digital signatures from dataset static const size_t RF_readDigitalSignatures; @@ -270,7 +270,7 @@ class DCMTK_DCMSR_EXPORT DSRTypes /// show the currently processed content item (e.g. "1.2.3") static const size_t RF_showCurrentlyProcessedItem; - //@} + ///@} /** @name renderHTML() flags @@ -278,7 +278,7 @@ class DCMTK_DCMSR_EXPORT DSRTypes * Please note that only the 'external' flags can be used from outside * this library. The 'shortcut' flags can be used for common combinations. */ - //@{ + ///@{ /// external: never expand child nodes inline static const size_t HF_neverExpandChildrenInline; @@ -351,14 +351,14 @@ class DCMTK_DCMSR_EXPORT DSRTypes /// shortcut: filter all flags that are only used internally static const size_t HF_internalUseOnly; - //@} + ///@} /** @name read/writeXML() flags * These flags can be combined and passed to the read/writeXML() methods. * The 'shortcut' flags can be used for common combinations. */ - //@{ + ///@{ /// write: write all tags even if their value is empty static const size_t XF_writeEmptyTags; @@ -401,14 +401,14 @@ class DCMTK_DCMSR_EXPORT DSRTypes /// shortcut: combines all XF_xxxAsAttribute write flags (see above) static const size_t XF_encodeEverythingAsAttribute; - //@} + ///@} /** @name print() flags * These flags can be combined and passed to the print() methods. * The 'shortcut' flags can be used for common combinations. */ - //@{ + ///@{ /// print item position ("1.2.3") instead of line indentation static const size_t PF_printItemPosition; @@ -460,13 +460,13 @@ class DCMTK_DCMSR_EXPORT DSRTypes /// shortcut: print all codes (combines all PF_printXxxCodes flags, see above) static const size_t PF_printAllCodes; - //@} + ///@} /** @name checkByReferenceRelationships() modes * These modes can be combined and passed to the checkByReferenceRelationships() method. */ - //@{ + ///@{ /// update the position string using the node ID static const size_t CM_updatePositionString; @@ -476,13 +476,13 @@ class DCMTK_DCMSR_EXPORT DSRTypes /// reset the reference target flag for all nodes static const size_t CM_resetReferenceTargetFlag; - //@} + ///@} /** @name checkByReferenceRelationships() bit masks * These bit masks are used to "filter" valid flags passed to checkByReferenceRelationships(). */ - //@{ + ///@{ /// bit mask (filter) for valid print flags (see PF_xxx) static const size_t CB_maskPrintFlags; @@ -490,7 +490,7 @@ class DCMTK_DCMSR_EXPORT DSRTypes /// bit mask (filter) for valid read flags (see RF_xxx) static const size_t CB_maskReadFlags; - //@} + ///@} // --- type definitions --- @@ -657,8 +657,12 @@ class DCMTK_DCMSR_EXPORT DSRTypes PT_MultipleVolumeRendering, /// Variable Modality LUT Softcopy Presentation State (VML-SPS) PT_VariableModalityLUT, + /// Waveform Presentation State (WPS) + PT_Waveform, + /// Waveform Acquisition Presentation State (WAPS) + PT_WaveformAcquisition, /// internal type used to mark the last entry - PT_last = PT_VariableModalityLUT + PT_last = PT_WaveformAcquisition }; /** SR graphic types. Used for content item SCOORD. diff --git a/dcmsr/include/dcmtk/dcmsr/dsrwavvl.h b/dcmsr/include/dcmtk/dcmsr/dsrwavvl.h index 3f7b5253..a64c3132 100644 --- a/dcmsr/include/dcmtk/dcmsr/dsrwavvl.h +++ b/dcmsr/include/dcmtk/dcmsr/dsrwavvl.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -145,6 +145,10 @@ class DCMTK_DCMSR_EXPORT DSRWaveformReferenceValue * Value is increased automatically by 1 after a new entry has been added. * @param flags flag used to customize the output (see DSRTypes::HF_xxx) * @param urlPrefix optional URL prefix used for hyperlink to referenced composite object + ** @note Please note that using parameter 'urlPrefix' can lead to security issues, as an + * attacker could misuse it to potentially inject dangerous content into the HTML/XHTML + * output. The value of this parameter is not checked. This is also true for derived + * classes. ** @return status, EC_Normal if successful, an error code otherwise */ virtual OFCondition renderHTML(STD_NAMESPACE ostream &docStream, diff --git a/dcmsr/include/dcmtk/dcmsr/dsrxmlc.h b/dcmsr/include/dcmtk/dcmsr/dsrxmlc.h index afd7a0c9..548089d0 100644 --- a/dcmsr/include/dcmtk/dcmsr/dsrxmlc.h +++ b/dcmsr/include/dcmtk/dcmsr/dsrxmlc.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2003-2024, OFFIS e.V. + * Copyright (C) 2003-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -25,10 +25,9 @@ #define DSRXMLC_H #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ - #include "dcmtk/dcmsr/dsdefine.h" - #include "dcmtk/ofstd/oftypes.h" /* for definition of OFBool */ +#include "dcmtk/ofstd/ofdiag.h" #ifdef WITH_LIBXML @@ -39,7 +38,13 @@ #define LIBXML_ATTR_FORMAT(fmt,args) #endif +// MacOS 15.5 defines some Clang specific pragmas in libxml header files. +// Suppress warnings caused by these pragmas when compiling with GCC. +#include DCMTK_DIAGNOSTIC_PUSH +#include DCMTK_DIAGNOSTIC_IGNORE_CLANG_PRAGMAS_ON_GCC #include +#include DCMTK_DIAGNOSTIC_POP + #endif diff --git a/dcmsr/libcmr/cid100.cc b/dcmsr/libcmr/cid100.cc index c1fc65c0..5a690c17 100644 --- a/dcmsr/libcmr/cid100.cc +++ b/dcmsr/libcmr/cid100.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID100_QuantitativeDiagnosticImagingProcedure * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:09 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:43 by J. Riesmeier * */ @@ -18,7 +18,8 @@ // general information on CID 100 (Quantitative Diagnostic Imaging Procedure) #define CONTEXT_GROUP_NUMBER "100" -#define CONTEXT_GROUP_VERSION "20230630" +#define CONTEXT_GROUP_KEYWORD "QuantitativeDiagnosticImagingProcedure" +#define CONTEXT_GROUP_VERSION "20250122" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.998" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID100_QuantitativeDiagnosticImagingProcedure::CodeList *CID100_QuantitativeDiag CID100_QuantitativeDiagnosticImagingProcedure::CID100_QuantitativeDiagnosticImagingProcedure(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID100_QuantitativeDiagnosticImagingProcedure::CID100_QuantitativeDiagnosticImag CID100_QuantitativeDiagnosticImagingProcedure::CID100_QuantitativeDiagnosticImagingProcedure(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -167,7 +168,6 @@ CID100_QuantitativeDiagnosticImagingProcedure::CodeList &CID100_QuantitativeDiag Codes->insert(OFMake_pair(PETCT_METImagingOfWholeBody, DSRBasicCodedEntry("764704008", "SCT", "PET/CT MET imaging of whole body"))); Codes->insert(OFMake_pair(CTPerfusionHeadWithContrastIV, DSRBasicCodedEntry("39142-5", "LN", "CT perfusion head with contrast IV"))); Codes->insert(OFMake_pair(SPECTBrain, DSRBasicCodedEntry("39632-5", "LN", "SPECT brain"))); - Codes->insert(OFMake_pair(NMHeadPerfusionBrainPET_CT_AV45, DSRBasicCodedEntry("RPID5427", "RADLEX", "NM head perfusion brain PET-CT AV-45"))); } /* should never be NULL */ return *Codes; diff --git a/dcmsr/libcmr/cid10013.cc b/dcmsr/libcmr/cid10013.cc index af30769e..9fbd5f99 100644 --- a/dcmsr/libcmr/cid10013.cc +++ b/dcmsr/libcmr/cid10013.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID10013_CTAcquisitionType * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:25 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:57 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 10013 (CT Acquisition Type) #define CONTEXT_GROUP_NUMBER "10013" +#define CONTEXT_GROUP_KEYWORD "CTAcquisitionType" #define CONTEXT_GROUP_VERSION "20160314" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.545" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID10013_CTAcquisitionType::CodeList *CID10013_CTAcquisitionType::Codes = NULL; CID10013_CTAcquisitionType::CID10013_CTAcquisitionType(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID10013_CTAcquisitionType::CID10013_CTAcquisitionType(const DSRCodedEntryValue CID10013_CTAcquisitionType::CID10013_CTAcquisitionType(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid10033.cc b/dcmsr/libcmr/cid10033.cc index 87e2531d..d098da6e 100644 --- a/dcmsr/libcmr/cid10033.cc +++ b/dcmsr/libcmr/cid10033.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID10033_CTReconstructionAlgorithm * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:26 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:58 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 10033 (CT Reconstruction Algorithm) #define CONTEXT_GROUP_NUMBER "10033" +#define CONTEXT_GROUP_KEYWORD "CTReconstructionAlgorithm" #define CONTEXT_GROUP_VERSION "20130207" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.958" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID10033_CTReconstructionAlgorithm::CodeList *CID10033_CTReconstructionAlgorithm CID10033_CTReconstructionAlgorithm::CID10033_CTReconstructionAlgorithm(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID10033_CTReconstructionAlgorithm::CID10033_CTReconstructionAlgorithm(const DSR CID10033_CTReconstructionAlgorithm::CID10033_CTReconstructionAlgorithm(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid11.cc b/dcmsr/libcmr/cid11.cc index e6e61071..6af4790e 100644 --- a/dcmsr/libcmr/cid11.cc +++ b/dcmsr/libcmr/cid11.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID11_AdministrationRoute * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:06 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:40 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 11 (Administration Route) #define CONTEXT_GROUP_NUMBER "11" +#define CONTEXT_GROUP_KEYWORD "AdministrationRoute" #define CONTEXT_GROUP_VERSION "20240611" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.9" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID11_AdministrationRoute::CodeList *CID11_AdministrationRoute::Codes = NULL; CID11_AdministrationRoute::CID11_AdministrationRoute(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID11_AdministrationRoute::CID11_AdministrationRoute(const DSRCodedEntryValue &s CID11_AdministrationRoute::CID11_AdministrationRoute(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid218.cc b/dcmsr/libcmr/cid218.cc index 96b3fa15..4d0bf001 100644 --- a/dcmsr/libcmr/cid218.cc +++ b/dcmsr/libcmr/cid218.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID218_QuantitativeImageFeature * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:10 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:44 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 218 (Quantitative Image Feature) #define CONTEXT_GROUP_NUMBER "218" +#define CONTEXT_GROUP_KEYWORD "QuantitativeImageFeature" #define CONTEXT_GROUP_VERSION "20200920" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.1269" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID218_QuantitativeImageFeature::CodeList *CID218_QuantitativeImageFeature::Code CID218_QuantitativeImageFeature::CID218_QuantitativeImageFeature(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID218_QuantitativeImageFeature::CID218_QuantitativeImageFeature(const DSRCodedE CID218_QuantitativeImageFeature::CID218_QuantitativeImageFeature(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid244.cc b/dcmsr/libcmr/cid244.cc index 6550718e..a77fd947 100644 --- a/dcmsr/libcmr/cid244.cc +++ b/dcmsr/libcmr/cid244.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID244_Laterality * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:11 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:45 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 244 (Laterality) #define CONTEXT_GROUP_NUMBER "244" +#define CONTEXT_GROUP_KEYWORD "Laterality" #define CONTEXT_GROUP_VERSION "20030108" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.37" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID244_Laterality::CodeList *CID244_Laterality::Codes = NULL; CID244_Laterality::CID244_Laterality(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID244_Laterality::CID244_Laterality(const DSRCodedEntryValue &selectedValue) CID244_Laterality::CID244_Laterality(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid247.cc b/dcmsr/libcmr/cid247.cc index 41640c58..b8ac53a9 100644 --- a/dcmsr/libcmr/cid247.cc +++ b/dcmsr/libcmr/cid247.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID247_LateralityLeftRightOnly * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:12 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:45 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 247 (Laterality Left-Right Only) #define CONTEXT_GROUP_NUMBER "247" +#define CONTEXT_GROUP_KEYWORD "LateralityLeftRightOnly" #define CONTEXT_GROUP_VERSION "20190524" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.1284" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID247_LateralityLeftRightOnly::CodeList *CID247_LateralityLeftRightOnly::Codes CID247_LateralityLeftRightOnly::CID247_LateralityLeftRightOnly(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID247_LateralityLeftRightOnly::CID247_LateralityLeftRightOnly(const DSRCodedEnt CID247_LateralityLeftRightOnly::CID247_LateralityLeftRightOnly(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid29.cc b/dcmsr/libcmr/cid29.cc index 6da71569..16f2ce5b 100644 --- a/dcmsr/libcmr/cid29.cc +++ b/dcmsr/libcmr/cid29.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID29_AcquisitionModality * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:07 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:41 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 29 (Acquisition Modality) #define CONTEXT_GROUP_NUMBER "29" +#define CONTEXT_GROUP_KEYWORD "AcquisitionModality" #define CONTEXT_GROUP_VERSION "20231115" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.19" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID29_AcquisitionModality::CodeList *CID29_AcquisitionModality::Codes = NULL; CID29_AcquisitionModality::CID29_AcquisitionModality(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID29_AcquisitionModality::CID29_AcquisitionModality(const DSRCodedEntryValue &s CID29_AcquisitionModality::CID29_AcquisitionModality(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -165,7 +166,7 @@ CID29_AcquisitionModality::CodeList &CID29_AcquisitionModality::getCodes() Codes->insert(OFMake_pair(IntravascularOpticalCoherenceTomography, DSRBasicCodedEntry("IVOCT", "DCM", "Intravascular Optical Coherence Tomography"))); Codes->insert(OFMake_pair(IntravascularUltrasound, DSRBasicCodedEntry("IVUS", "DCM", "Intravascular Ultrasound"))); Codes->insert(OFMake_pair(Keratometry, DSRBasicCodedEntry("KER", "DCM", "Keratometry"))); - Codes->insert(OFMake_pair(LaserScan, DSRBasicCodedEntry("LS", "DCM", "Laser Scan"))); + Codes->insert(OFMake_pair(LaserSurfaceScan, DSRBasicCodedEntry("LS", "DCM", "Laser surface scan"))); Codes->insert(OFMake_pair(Lensometry, DSRBasicCodedEntry("LEN", "DCM", "Lensometry"))); Codes->insert(OFMake_pair(MagneticResonance, DSRBasicCodedEntry("MR", "DCM", "Magnetic Resonance"))); Codes->insert(OFMake_pair(Mammography, DSRBasicCodedEntry("MG", "DCM", "Mammography"))); diff --git a/dcmsr/libcmr/cid4020.cc b/dcmsr/libcmr/cid4020.cc index 88e913ef..89cf9904 100644 --- a/dcmsr/libcmr/cid4020.cc +++ b/dcmsr/libcmr/cid4020.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID4020_PETRadionuclide * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:13 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:46 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 4020 (PET Radionuclide) #define CONTEXT_GROUP_NUMBER "4020" +#define CONTEXT_GROUP_KEYWORD "PETRadionuclide" #define CONTEXT_GROUP_VERSION "20160119" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.304" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID4020_PETRadionuclide::CodeList *CID4020_PETRadionuclide::Codes = NULL; CID4020_PETRadionuclide::CID4020_PETRadionuclide(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID4020_PETRadionuclide::CID4020_PETRadionuclide(const DSRCodedEntryValue &selec CID4020_PETRadionuclide::CID4020_PETRadionuclide(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid4021.cc b/dcmsr/libcmr/cid4021.cc index fee7a975..47ab072d 100644 --- a/dcmsr/libcmr/cid4021.cc +++ b/dcmsr/libcmr/cid4021.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID4021_PETRadiopharmaceutical * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:14 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:47 by J. Riesmeier * */ @@ -18,7 +18,8 @@ // general information on CID 4021 (PET Radiopharmaceutical) #define CONTEXT_GROUP_NUMBER "4021" -#define CONTEXT_GROUP_VERSION "20221201" +#define CONTEXT_GROUP_KEYWORD "PETRadiopharmaceutical" +#define CONTEXT_GROUP_VERSION "20251111" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.305" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID4021_PETRadiopharmaceutical::CodeList *CID4021_PETRadiopharmaceutical::Codes CID4021_PETRadiopharmaceutical::CID4021_PETRadiopharmaceutical(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID4021_PETRadiopharmaceutical::CID4021_PETRadiopharmaceutical(const DSRCodedEnt CID4021_PETRadiopharmaceutical::CID4021_PETRadiopharmaceutical(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -151,6 +152,8 @@ CID4021_PETRadiopharmaceutical::CodeList &CID4021_PETRadiopharmaceutical::getCod /* and initialize it by adding the coded entries */ Codes->insert(OFMake_pair(_28H1_89Zr, DSRBasicCodedEntry("126752", "DCM", "28H1 ^89^Zr"))); Codes->insert(OFMake_pair(_2FA_F18, DSRBasicCodedEntry("126713", "DCM", "2FA F^18^"))); + Codes->insert(OFMake_pair(_2Thymidine_C11, DSRBasicCodedEntry("C90936", "NCIt", "2-Thymidine C^11^"))); + Codes->insert(OFMake_pair(_3NMethylspiperone_C11, DSRBasicCodedEntry("771875003", "SCT", "3-N-Methylspiperone C^11^"))); Codes->insert(OFMake_pair(_7D12_89Zr, DSRBasicCodedEntry("126751", "DCM", "7D12 ^89^Zr"))); Codes->insert(OFMake_pair(_7E11_89Zr, DSRBasicCodedEntry("126750", "DCM", "7E11 ^89^Zr"))); Codes->insert(OFMake_pair(Acetate_C11, DSRBasicCodedEntry("129513004", "SCT", "Acetate C^11^"))); @@ -177,14 +180,15 @@ CID4021_PETRadiopharmaceutical::CodeList &CID4021_PETRadiopharmaceutical::getCod Codes->insert(OFMake_pair(CLR1404_I131, DSRBasicCodedEntry("126716", "DCM", "CLR1404 I^131^"))); Codes->insert(OFMake_pair(CMAbU36_89Zr, DSRBasicCodedEntry("126746", "DCM", "cMAb U36 ^89^Zr"))); Codes->insert(OFMake_pair(CU36_89Zr, DSRBasicCodedEntry("126515", "DCM", "cU36 ^89^Zr"))); + Codes->insert(OFMake_pair(DASB_C11, DSRBasicCodedEntry("C412822", "MSH", "DASB C^11^"))); Codes->insert(OFMake_pair(DCFBC_F18, DSRBasicCodedEntry("C96234", "NCIt", "DCFBC F^18^"))); - Codes->insert(OFMake_pair(Piflufolastat_F18, DSRBasicCodedEntry("C116352", "NCIt", "Piflufolastat F^18^"))); Codes->insert(OFMake_pair(DfFK2_89Zr, DSRBasicCodedEntry("126762", "DCM", "Df-[FK](2) ^89^Zr"))); Codes->insert(OFMake_pair(DfFK23PEG4_89Zr, DSRBasicCodedEntry("126763", "DCM", "Df-[FK](2)-3PEG(4) ^89^Zr"))); Codes->insert(OFMake_pair(DfCD45_89Zr, DSRBasicCodedEntry("126520", "DCM", "Df-CD45 ^89^Zr"))); Codes->insert(OFMake_pair(DfFK_89Zr, DSRBasicCodedEntry("126760", "DCM", "Df-FK ^89^Zr"))); Codes->insert(OFMake_pair(DfFKPEG3_89Zr, DSRBasicCodedEntry("126761", "DCM", "Df-FK-PEG(3) ^89^Zr"))); Codes->insert(OFMake_pair(DN30_89Zr, DSRBasicCodedEntry("126747", "DCM", "DN30 ^89^Zr"))); + Codes->insert(OFMake_pair(Dotatate_Ga68, DSRBasicCodedEntry("724025002", "SCT", "Dotatate Ga^68^"))); Codes->insert(OFMake_pair(DPA713_11C, DSRBasicCodedEntry("126765", "DCM", "DPA-713 ^11^C"))); Codes->insert(OFMake_pair(DPA714_18F, DSRBasicCodedEntry("126766", "DCM", "DPA-714 ^18^F"))); Codes->insert(OFMake_pair(E4G10_89Zr, DSRBasicCodedEntry("126519", "DCM", "E4G10 ^89^Zr"))); @@ -203,6 +207,7 @@ CID4021_PETRadiopharmaceutical::CodeList &CID4021_PETRadiopharmaceutical::getCod Codes->insert(OFMake_pair(Flumazenil_C11, DSRBasicCodedEntry("423543007", "SCT", "Flumazenil C^11^"))); Codes->insert(OFMake_pair(Flumazenil_F18, DSRBasicCodedEntry("422975006", "SCT", "Flumazenil F^18^"))); Codes->insert(OFMake_pair(Fluorethyltyrosin_F18, DSRBasicCodedEntry("424708001", "SCT", "Fluorethyltyrosin F^18^"))); + Codes->insert(OFMake_pair(FluoroazomycinArabinoside_F18, DSRBasicCodedEntry("C62520", "NCIt", "Fluoroazomycin arabinoside F^18^"))); Codes->insert(OFMake_pair(Fluorobenzothiazole_F18, DSRBasicCodedEntry("423546004", "SCT", "Fluorobenzothiazole F^18^"))); Codes->insert(OFMake_pair(Fluorocholine_F18, DSRBasicCodedEntry("456992002", "SCT", "Fluorocholine F^18^"))); Codes->insert(OFMake_pair(Fluorodeoxyglucose_F18, DSRBasicCodedEntry("35321007", "SCT", "Fluorodeoxyglucose F^18^"))); @@ -212,6 +217,7 @@ CID4021_PETRadiopharmaceutical::CodeList &CID4021_PETRadiopharmaceutical::getCod Codes->insert(OFMake_pair(Fluoromethane_F18, DSRBasicCodedEntry("422763008", "SCT", "Fluoromethane F^18^"))); Codes->insert(OFMake_pair(Fluoromisonidazole_F18, DSRBasicCodedEntry("422598008", "SCT", "Fluoromisonidazole F^18^"))); Codes->insert(OFMake_pair(FluoropropylDihydrotetrabenazine_F18, DSRBasicCodedEntry("C2934038", "UMLS", "Fluoropropyl-dihydrotetrabenazine F^18^"))); + Codes->insert(OFMake_pair(Fluorothymidine_F18, DSRBasicCodedEntry("764937002", "SCT", "Fluorothymidine F^18^"))); Codes->insert(OFMake_pair(Fluorotriopride_F18, DSRBasicCodedEntry("126707", "DCM", "Fluorotriopride F^18^"))); Codes->insert(OFMake_pair(Fluorouracil_F18, DSRBasicCodedEntry("425236000", "SCT", "Fluorouracil F^18^"))); Codes->insert(OFMake_pair(Flurpiridaz_F18, DSRBasicCodedEntry("126718", "DCM", "Flurpiridaz F^18^"))); @@ -220,6 +226,7 @@ CID4021_PETRadiopharmaceutical::CodeList &CID4021_PETRadiopharmaceutical::getCod Codes->insert(OFMake_pair(GA201_89Zr, DSRBasicCodedEntry("126731", "DCM", "GA201 ^89^Zr"))); Codes->insert(OFMake_pair(Germanium_Ge68, DSRBasicCodedEntry("53315004", "SCT", "Germanium Ge^68^"))); Codes->insert(OFMake_pair(GlembatumumabVedotin_89Zr, DSRBasicCodedEntry("126724", "DCM", "Glembatumumab vedotin ^89^Zr"))); + Codes->insert(OFMake_pair(Glucose_C11, DSRBasicCodedEntry("126521", "DCM", "Glucose C^11^"))); Codes->insert(OFMake_pair(Glutamate_N13, DSRBasicCodedEntry("129509006", "SCT", "Glutamate N^13^"))); Codes->insert(OFMake_pair(Glutamine_C11, DSRBasicCodedEntry("126709", "DCM", "Glutamine C^11^"))); Codes->insert(OFMake_pair(Glutamine_C14, DSRBasicCodedEntry("126710", "DCM", "Glutamine C^14^"))); @@ -244,6 +251,9 @@ CID4021_PETRadiopharmaceutical::CodeList &CID4021_PETRadiopharmaceutical::getCod Codes->insert(OFMake_pair(Palmitate_C11, DSRBasicCodedEntry("129514005", "SCT", "Palmitate C^11^"))); Codes->insert(OFMake_pair(Panitumumab_89Zr, DSRBasicCodedEntry("126736", "DCM", "Panitumumab ^89^Zr"))); Codes->insert(OFMake_pair(Pegdinetanib_89Zr, DSRBasicCodedEntry("126728", "DCM", "Pegdinetanib ^89^Zr"))); + Codes->insert(OFMake_pair(Pembrolizumab_89Zr, DSRBasicCodedEntry("C148167", "NCIt", "Pembrolizumab ^89^Zr"))); + Codes->insert(OFMake_pair(PI2620_F18, DSRBasicCodedEntry("C5433257", "UMLS", "PI-2620 F^18^"))); + Codes->insert(OFMake_pair(Piflufolastat_F18, DSRBasicCodedEntry("C116352", "NCIt", "Piflufolastat F^18^"))); Codes->insert(OFMake_pair(PinatuzumabVedotin_89Zr, DSRBasicCodedEntry("126725", "DCM", "Pinatuzumab vedotin ^89^Zr"))); Codes->insert(OFMake_pair(PittsburghCompoundB_C11, DSRBasicCodedEntry("126500", "DCM", "Pittsburgh compound B C^11^"))); Codes->insert(OFMake_pair(PK11195_11C, DSRBasicCodedEntry("C1609883", "UMLS", "PK11195 ^11^C"))); @@ -272,10 +282,8 @@ CID4021_PETRadiopharmaceutical::CodeList &CID4021_PETRadiopharmaceutical::getCod Codes->insert(OFMake_pair(T807_F18, DSRBasicCodedEntry("126502", "DCM", "T807 F^18^"))); Codes->insert(OFMake_pair(THK5317_F18, DSRBasicCodedEntry("C4550127", "UMLS", "THK5317 F^18^"))); Codes->insert(OFMake_pair(THK5351_F18, DSRBasicCodedEntry("C4279748", "UMLS", "THK5351 F^18^"))); - Codes->insert(OFMake_pair(Thymidine_F18, DSRBasicCodedEntry("129502002", "SCT", "Thymidine F^18^"))); Codes->insert(OFMake_pair(Trastuzumab_89Zr, DSRBasicCodedEntry("126512", "DCM", "Trastuzumab ^89^Zr"))); Codes->insert(OFMake_pair(TRC105_89Zr, DSRBasicCodedEntry("126749", "DCM", "TRC105 ^89^Zr"))); - Codes->insert(OFMake_pair(Dotatate_Ga68, DSRBasicCodedEntry("724025002", "SCT", "Dotatate Ga^68^"))); Codes->insert(OFMake_pair(Ublituximab_89Zr, DSRBasicCodedEntry("126739", "DCM", "Ublituximab ^89^Zr"))); Codes->insert(OFMake_pair(UCBJ_C11, DSRBasicCodedEntry("C4506788", "UMLS", "UCB-J C^11^"))); Codes->insert(OFMake_pair(XmAb5574_89Zr, DSRBasicCodedEntry("126734", "DCM", "XmAb5574 ^89^Zr"))); diff --git a/dcmsr/libcmr/cid4031.cc b/dcmsr/libcmr/cid4031.cc index ec254824..4cdedd11 100644 --- a/dcmsr/libcmr/cid4031.cc +++ b/dcmsr/libcmr/cid4031.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID4031_CommonAnatomicRegion * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:15 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:48 by J. Riesmeier * */ @@ -18,7 +18,8 @@ // general information on CID 4031 (Common Anatomic Region) #define CONTEXT_GROUP_NUMBER "4031" -#define CONTEXT_GROUP_VERSION "20221224" +#define CONTEXT_GROUP_KEYWORD "CommonAnatomicRegion" +#define CONTEXT_GROUP_VERSION "20250709" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.308" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID4031_CommonAnatomicRegion::CodeList *CID4031_CommonAnatomicRegion::Codes = NU CID4031_CommonAnatomicRegion::CID4031_CommonAnatomicRegion(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID4031_CommonAnatomicRegion::CID4031_CommonAnatomicRegion(const DSRCodedEntryVa CID4031_CommonAnatomicRegion::CID4031_CommonAnatomicRegion(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -163,11 +164,12 @@ CID4031_CommonAnatomicRegion::CodeList &CID4031_CommonAnatomicRegion::getCodes() Codes->insert(OFMake_pair(Breast, DSRBasicCodedEntry("76752008", "SCT", "Breast"))); Codes->insert(OFMake_pair(Bronchus, DSRBasicCodedEntry("955009", "SCT", "Bronchus"))); Codes->insert(OFMake_pair(Calcaneus, DSRBasicCodedEntry("80144004", "SCT", "Calcaneus"))); + Codes->insert(OFMake_pair(CardiovascularSystem, DSRBasicCodedEntry("113257007", "SCT", "Cardiovascular system"))); Codes->insert(OFMake_pair(CervicalSpine, DSRBasicCodedEntry("122494005", "SCT", "Cervical spine"))); Codes->insert(OFMake_pair(CervicoThoracicSpine, DSRBasicCodedEntry("1217257000", "SCT", "Cervico-thoracic spine"))); Codes->insert(OFMake_pair(Chest, DSRBasicCodedEntry("816094009", "SCT", "Chest"))); - Codes->insert(OFMake_pair(ChestAndAbdomen, DSRBasicCodedEntry("416550000", "SCT", "Chest and Abdomen"))); Codes->insert(OFMake_pair(ChestAbdomenAndPelvis, DSRBasicCodedEntry("416775004", "SCT", "Chest, Abdomen and Pelvis"))); + Codes->insert(OFMake_pair(ChestAndAbdomen, DSRBasicCodedEntry("416550000", "SCT", "Chest and Abdomen"))); Codes->insert(OFMake_pair(Clavicle, DSRBasicCodedEntry("51299004", "SCT", "Clavicle"))); Codes->insert(OFMake_pair(Coccyx, DSRBasicCodedEntry("64688005", "SCT", "Coccyx"))); Codes->insert(OFMake_pair(Colon, DSRBasicCodedEntry("71854001", "SCT", "Colon"))); @@ -205,6 +207,7 @@ CID4031_CommonAnatomicRegion::CodeList &CID4031_CommonAnatomicRegion::getCodes() Codes->insert(OFMake_pair(LiverAndBiliaryStructure, DSRBasicCodedEntry("303270005", "SCT", "Liver and biliary structure"))); Codes->insert(OFMake_pair(LowerLeg, DSRBasicCodedEntry("30021000", "SCT", "Lower leg"))); Codes->insert(OFMake_pair(LowerLimb, DSRBasicCodedEntry("61685007", "SCT", "Lower limb"))); + Codes->insert(OFMake_pair(LowerTrunk, DSRBasicCodedEntry("63337009", "SCT", "Lower trunk"))); Codes->insert(OFMake_pair(LumbarSpine, DSRBasicCodedEntry("122496007", "SCT", "Lumbar spine"))); Codes->insert(OFMake_pair(LumboSacralSpine, DSRBasicCodedEntry("1217253001", "SCT", "Lumbo-sacral spine"))); Codes->insert(OFMake_pair(Mandible, DSRBasicCodedEntry("91609006", "SCT", "Mandible"))); @@ -215,9 +218,9 @@ CID4031_CommonAnatomicRegion::CodeList &CID4031_CommonAnatomicRegion::getCodes() Codes->insert(OFMake_pair(MuscleOfUpperLimb, DSRBasicCodedEntry("30608006", "SCT", "Muscle of upper limb"))); Codes->insert(OFMake_pair(NasalBone, DSRBasicCodedEntry("74386004", "SCT", "Nasal bone"))); Codes->insert(OFMake_pair(Neck, DSRBasicCodedEntry("45048000", "SCT", "Neck"))); - Codes->insert(OFMake_pair(NeckAndChest, DSRBasicCodedEntry("417437006", "SCT", "Neck and Chest"))); - Codes->insert(OFMake_pair(NeckChestAndAbdomen, DSRBasicCodedEntry("416152001", "SCT", "Neck, Chest and Abdomen"))); Codes->insert(OFMake_pair(NeckChestAbdomenAndPelvis, DSRBasicCodedEntry("416319003", "SCT", "Neck, Chest, Abdomen and Pelvis"))); + Codes->insert(OFMake_pair(NeckChestAndAbdomen, DSRBasicCodedEntry("416152001", "SCT", "Neck, Chest and Abdomen"))); + Codes->insert(OFMake_pair(NeckAndChest, DSRBasicCodedEntry("417437006", "SCT", "Neck and Chest"))); Codes->insert(OFMake_pair(OpticCanal, DSRBasicCodedEntry("55024004", "SCT", "Optic canal"))); Codes->insert(OFMake_pair(OrbitalStructure, DSRBasicCodedEntry("363654007", "SCT", "Orbital structure"))); Codes->insert(OFMake_pair(Pancreas, DSRBasicCodedEntry("15776009", "SCT", "Pancreas"))); @@ -242,6 +245,7 @@ CID4031_CommonAnatomicRegion::CodeList &CID4031_CommonAnatomicRegion::getCodes() Codes->insert(OFMake_pair(Skull, DSRBasicCodedEntry("89546000", "SCT", "Skull"))); Codes->insert(OFMake_pair(SmallIntestine, DSRBasicCodedEntry("30315005", "SCT", "Small intestine"))); Codes->insert(OFMake_pair(Spine, DSRBasicCodedEntry("421060004", "SCT", "Spine"))); + Codes->insert(OFMake_pair(SpineAndPerOrCord, DSRBasicCodedEntry("737561001", "SCT", "Spine and/or cord"))); Codes->insert(OFMake_pair(SternoclavicularJoint, DSRBasicCodedEntry("7844006", "SCT", "Sternoclavicular joint"))); Codes->insert(OFMake_pair(Sternum, DSRBasicCodedEntry("56873002", "SCT", "Sternum"))); Codes->insert(OFMake_pair(Stomach, DSRBasicCodedEntry("69695003", "SCT", "Stomach"))); @@ -254,8 +258,10 @@ CID4031_CommonAnatomicRegion::CodeList &CID4031_CommonAnatomicRegion::getCodes() Codes->insert(OFMake_pair(Thumb, DSRBasicCodedEntry("76505004", "SCT", "Thumb"))); Codes->insert(OFMake_pair(Toe, DSRBasicCodedEntry("29707007", "SCT", "Toe"))); Codes->insert(OFMake_pair(Trachea, DSRBasicCodedEntry("44567001", "SCT", "Trachea"))); + Codes->insert(OFMake_pair(Trunk, DSRBasicCodedEntry("22943007", "SCT", "Trunk"))); Codes->insert(OFMake_pair(UpperArm, DSRBasicCodedEntry("40983000", "SCT", "Upper arm"))); Codes->insert(OFMake_pair(UpperLimb, DSRBasicCodedEntry("53120007", "SCT", "Upper limb"))); + Codes->insert(OFMake_pair(UpperTrunk, DSRBasicCodedEntry("67734004", "SCT", "Upper trunk"))); Codes->insert(OFMake_pair(UpperUrinaryTract, DSRBasicCodedEntry("431491007", "SCT", "Upper urinary tract"))); Codes->insert(OFMake_pair(Ureter, DSRBasicCodedEntry("87953007", "SCT", "Ureter"))); Codes->insert(OFMake_pair(Urethra, DSRBasicCodedEntry("13648007", "SCT", "Urethra"))); diff --git a/dcmsr/libcmr/cid42.cc b/dcmsr/libcmr/cid42.cc index 73e7fd06..a9970ad0 100644 --- a/dcmsr/libcmr/cid42.cc +++ b/dcmsr/libcmr/cid42.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID42_NumericValueQualifier * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:08 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:42 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 42 (Numeric Value Qualifier) #define CONTEXT_GROUP_NUMBER "42" +#define CONTEXT_GROUP_KEYWORD "NumericValueQualifier" #define CONTEXT_GROUP_VERSION "20020114" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.22" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID42_NumericValueQualifier::CodeList *CID42_NumericValueQualifier::Codes = NULL CID42_NumericValueQualifier::CID42_NumericValueQualifier(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID42_NumericValueQualifier::CID42_NumericValueQualifier(const DSRCodedEntryValu CID42_NumericValueQualifier::CID42_NumericValueQualifier(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid5000.cc b/dcmsr/libcmr/cid5000.cc index 45974a02..f965fb37 100644 --- a/dcmsr/libcmr/cid5000.cc +++ b/dcmsr/libcmr/cid5000.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2022, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID5000_Language @@ -17,6 +17,7 @@ // general information on CID 5000 (Language) #define CONTEXT_GROUP_NUMBER "5000" +#define CONTEXT_GROUP_KEYWORD "Language" #define CONTEXT_GROUP_VERSION "" /* unknown */ #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.328" #define CONTEXT_GROUP_TYPE OFTrue /* extensible? */ @@ -26,7 +27,7 @@ CID5000_Language::CodeList *CID5000_Language::Codes = NULL; CID5000_Language::CID5000_Language(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -34,7 +35,7 @@ CID5000_Language::CID5000_Language(const DSRCodedEntryValue &selectedValue) CID5000_Language::CID5000_Language(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid5001.cc b/dcmsr/libcmr/cid5001.cc index 28d9d945..e70ec8f8 100644 --- a/dcmsr/libcmr/cid5001.cc +++ b/dcmsr/libcmr/cid5001.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2022, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID5001_Country @@ -17,6 +17,7 @@ // general information on CID 5001 (Country) #define CONTEXT_GROUP_NUMBER "5001" +#define CONTEXT_GROUP_KEYWORD "Country" #define CONTEXT_GROUP_VERSION "" /* unknown */ #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.329" #define CONTEXT_GROUP_TYPE OFTrue /* extensible? */ @@ -26,7 +27,7 @@ CID5001_Country::CodeList *CID5001_Country::Codes = NULL; CID5001_Country::CID5001_Country(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -34,7 +35,7 @@ CID5001_Country::CID5001_Country(const DSRCodedEntryValue &selectedValue) CID5001_Country::CID5001_Country(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid6147.cc b/dcmsr/libcmr/cid6147.cc index 9a58e1e7..1bfaa746 100644 --- a/dcmsr/libcmr/cid6147.cc +++ b/dcmsr/libcmr/cid6147.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID6147_ResponseCriteria * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:16 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:49 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 6147 (Response Criteria) #define CONTEXT_GROUP_NUMBER "6147" +#define CONTEXT_GROUP_KEYWORD "ResponseCriteria" #define CONTEXT_GROUP_VERSION "20141110" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.1004" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID6147_ResponseCriteria::CodeList *CID6147_ResponseCriteria::Codes = NULL; CID6147_ResponseCriteria::CID6147_ResponseCriteria(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID6147_ResponseCriteria::CID6147_ResponseCriteria(const DSRCodedEntryValue &sel CID6147_ResponseCriteria::CID6147_ResponseCriteria(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid7021.cc b/dcmsr/libcmr/cid7021.cc index d70b58a3..97e492ca 100644 --- a/dcmsr/libcmr/cid7021.cc +++ b/dcmsr/libcmr/cid7021.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID7021_MeasurementReportDocumentTitle * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:17 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:50 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 7021 (Measurement Report Document Title) #define CONTEXT_GROUP_NUMBER "7021" +#define CONTEXT_GROUP_KEYWORD "MeasurementReportDocumentTitle" #define CONTEXT_GROUP_VERSION "20141110" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.997" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID7021_MeasurementReportDocumentTitle::CodeList *CID7021_MeasurementReportDocum CID7021_MeasurementReportDocumentTitle::CID7021_MeasurementReportDocumentTitle(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID7021_MeasurementReportDocumentTitle::CID7021_MeasurementReportDocumentTitle(c CID7021_MeasurementReportDocumentTitle::CID7021_MeasurementReportDocumentTitle(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid7181.cc b/dcmsr/libcmr/cid7181.cc index 200328b3..fed9f931 100644 --- a/dcmsr/libcmr/cid7181.cc +++ b/dcmsr/libcmr/cid7181.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID7181_AbstractMultiDimensionalImageModelComponentUnit * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:18 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:51 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 7181 (Abstract Multi-dimensional Image Model Component Unit) #define CONTEXT_GROUP_NUMBER "7181" +#define CONTEXT_GROUP_KEYWORD "AbstractMultiDimensionalImageModelComponentUnit" #define CONTEXT_GROUP_VERSION "20180605" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.918" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID7181_AbstractMultiDimensionalImageModelComponentUnit::CodeList *CID7181_Abstr CID7181_AbstractMultiDimensionalImageModelComponentUnit::CID7181_AbstractMultiDimensionalImageModelComponentUnit(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID7181_AbstractMultiDimensionalImageModelComponentUnit::CID7181_AbstractMultiDi CID7181_AbstractMultiDimensionalImageModelComponentUnit::CID7181_AbstractMultiDimensionalImageModelComponentUnit(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid7445.cc b/dcmsr/libcmr/cid7445.cc index fc05b84e..c411fe54 100644 --- a/dcmsr/libcmr/cid7445.cc +++ b/dcmsr/libcmr/cid7445.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID7445_DeviceParticipatingRole * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:19 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:52 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 7445 (Device Participating Role) #define CONTEXT_GROUP_NUMBER "7445" +#define CONTEXT_GROUP_KEYWORD "DeviceParticipatingRole" #define CONTEXT_GROUP_VERSION "20120406" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.1042" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID7445_DeviceParticipatingRole::CodeList *CID7445_DeviceParticipatingRole::Code CID7445_DeviceParticipatingRole::CID7445_DeviceParticipatingRole(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID7445_DeviceParticipatingRole::CID7445_DeviceParticipatingRole(const DSRCodedE CID7445_DeviceParticipatingRole::CID7445_DeviceParticipatingRole(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid7452.cc b/dcmsr/libcmr/cid7452.cc index 8bdcc10e..36086099 100644 --- a/dcmsr/libcmr/cid7452.cc +++ b/dcmsr/libcmr/cid7452.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID7452_OrganizationalRole * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:20 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:52 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 7452 (Organizational Role) #define CONTEXT_GROUP_NUMBER "7452" +#define CONTEXT_GROUP_KEYWORD "OrganizationalRole" #define CONTEXT_GROUP_VERSION "20170626" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.516" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID7452_OrganizationalRole::CodeList *CID7452_OrganizationalRole::Codes = NULL; CID7452_OrganizationalRole::CID7452_OrganizationalRole(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID7452_OrganizationalRole::CID7452_OrganizationalRole(const DSRCodedEntryValue CID7452_OrganizationalRole::CID7452_OrganizationalRole(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid7453.cc b/dcmsr/libcmr/cid7453.cc index ce6a674e..ce89897a 100644 --- a/dcmsr/libcmr/cid7453.cc +++ b/dcmsr/libcmr/cid7453.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID7453_PerformingRole * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:21 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:53 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 7453 (Performing Role) #define CONTEXT_GROUP_NUMBER "7453" +#define CONTEXT_GROUP_KEYWORD "PerformingRole" #define CONTEXT_GROUP_VERSION "20180326" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.517" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID7453_PerformingRole::CodeList *CID7453_PerformingRole::Codes = NULL; CID7453_PerformingRole::CID7453_PerformingRole(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID7453_PerformingRole::CID7453_PerformingRole(const DSRCodedEntryValue &selecte CID7453_PerformingRole::CID7453_PerformingRole(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid7464.cc b/dcmsr/libcmr/cid7464.cc index c91f8780..7a56d812 100644 --- a/dcmsr/libcmr/cid7464.cc +++ b/dcmsr/libcmr/cid7464.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID7464_GeneralRegionOfInterestMeasurementModifier * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:22 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:54 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 7464 (General Region of Interest Measurement Modifier) #define CONTEXT_GROUP_NUMBER "7464" +#define CONTEXT_GROUP_KEYWORD "GeneralRegionOfInterestMeasurementModifier" #define CONTEXT_GROUP_VERSION "20121101" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.951" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID7464_GeneralRegionOfInterestMeasurementModifier::CodeList *CID7464_GeneralReg CID7464_GeneralRegionOfInterestMeasurementModifier::CID7464_GeneralRegionOfInterestMeasurementModifier(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID7464_GeneralRegionOfInterestMeasurementModifier::CID7464_GeneralRegionOfInter CID7464_GeneralRegionOfInterestMeasurementModifier::CID7464_GeneralRegionOfInterestMeasurementModifier(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid7469.cc b/dcmsr/libcmr/cid7469.cc index 81466ba5..1a53f0c8 100644 --- a/dcmsr/libcmr/cid7469.cc +++ b/dcmsr/libcmr/cid7469.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID7469_GenericIntensityAndSizeMeasurement * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:23 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:55 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 7469 (Generic Intensity and Size Measurement) #define CONTEXT_GROUP_NUMBER "7469" +#define CONTEXT_GROUP_KEYWORD "GenericIntensityAndSizeMeasurement" #define CONTEXT_GROUP_VERSION "20240913" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.1003" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID7469_GenericIntensityAndSizeMeasurement::CodeList *CID7469_GenericIntensityAn CID7469_GenericIntensityAndSizeMeasurement::CID7469_GenericIntensityAndSizeMeasurement(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID7469_GenericIntensityAndSizeMeasurement::CID7469_GenericIntensityAndSizeMeasu CID7469_GenericIntensityAndSizeMeasurement::CID7469_GenericIntensityAndSizeMeasurement(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libcmr/cid7551.cc b/dcmsr/libcmr/cid7551.cc index 22c79cc1..64b3bf2c 100644 --- a/dcmsr/libcmr/cid7551.cc +++ b/dcmsr/libcmr/cid7551.cc @@ -1,12 +1,12 @@ /* * - * Copyright (C) 2015-2024, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class CID7551_GenericPurposeOfReferenceToImagesAndCoordinatesInMeasurement * - * Generated automatically from DICOM PS 3.16-2024d - * File created on 2024-10-08 10:25:24 by J. Riesmeier + * Generated automatically from DICOM PS 3.16-2025e + * File created on 2025-11-21 12:16:56 by J. Riesmeier * */ @@ -18,6 +18,7 @@ // general information on CID 7551 (Generic Purpose of Reference to Images and Coordinates in Measurement) #define CONTEXT_GROUP_NUMBER "7551" +#define CONTEXT_GROUP_KEYWORD "GenericPurposeOfReferenceToImagesAndCoordinatesInMeasurement" #define CONTEXT_GROUP_VERSION "20200920" #define CONTEXT_GROUP_UID "1.2.840.10008.6.1.1343" #define CONTEXT_GROUP_TYPE OFTrue /* extensible */ @@ -27,7 +28,7 @@ CID7551_GenericPurposeOfReferenceToImagesAndCoordinatesInMeasurement::CodeList * CID7551_GenericPurposeOfReferenceToImagesAndCoordinatesInMeasurement::CID7551_GenericPurposeOfReferenceToImagesAndCoordinatesInMeasurement(const DSRCodedEntryValue &selectedValue) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, selectedValue) { setExtensible(CONTEXT_GROUP_TYPE); } @@ -35,7 +36,7 @@ CID7551_GenericPurposeOfReferenceToImagesAndCoordinatesInMeasurement::CID7551_Ge CID7551_GenericPurposeOfReferenceToImagesAndCoordinatesInMeasurement::CID7551_GenericPurposeOfReferenceToImagesAndCoordinatesInMeasurement(const EnumType selectedValue, const OFBool enhancedEncodingMode) - : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) + : DSRContextGroup(CONTEXT_GROUP_NUMBER, "DCMR", CONTEXT_GROUP_KEYWORD, CONTEXT_GROUP_VERSION, CONTEXT_GROUP_UID, getCodedEntry(selectedValue, enhancedEncodingMode)) { setExtensible(CONTEXT_GROUP_TYPE); } diff --git a/dcmsr/libsrc/Makefile.dep b/dcmsr/libsrc/Makefile.dep index 33344c72..b6289371 100644 --- a/dcmsr/libsrc/Makefile.dep +++ b/dcmsr/libsrc/Makefile.dep @@ -1355,8 +1355,9 @@ dsrdoc.o: dsrdoc.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmsr/dsrwavch.h ../include/dcmtk/dcmsr/dsrrtpl.h \ ../include/dcmtk/dcmsr/dsrctpl.h ../include/dcmtk/dcmsr/dsrsoprf.h \ ../include/dcmtk/dcmsr/dsrrefin.h ../include/dcmtk/dcmsr/dsrcsidl.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrda.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ @@ -2061,6 +2062,7 @@ dsrimgvl.o: dsrimgvl.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dipixel.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimomod.h \ ../../dcmimgle/include/dcmtk/dcmimgle/diluptab.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrobow.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dibaslut.h \ ../../dcmimgle/include/dcmtk/dcmimgle/dimoopx.h \ ../../dcmimgle/include/dcmtk/dcmimgle/didispfn.h \ @@ -5283,7 +5285,8 @@ dsrxmlc.o: dsrxmlc.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/ofcast.h \ ../../ofstd/include/dcmtk/ofstd/ofexport.h \ ../../ofstd/include/dcmtk/ofstd/oftypes.h \ - ../../ofstd/include/dcmtk/ofstd/ofstdinc.h + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h dsrxmld.o: dsrxmld.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmsr/dsrxmld.h ../include/dcmtk/dcmsr/dsrtypes.h \ ../include/dcmtk/dcmsr/dsdefine.h \ diff --git a/dcmsr/libsrc/dsrctxgr.cc b/dcmsr/libsrc/dsrctxgr.cc index f9585b1d..f3ab5db2 100644 --- a/dcmsr/libsrc/dsrctxgr.cc +++ b/dcmsr/libsrc/dsrctxgr.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2016, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -28,11 +28,13 @@ DSRContextGroup::DSRContextGroup(const OFString &contextIdentifier, const OFString &mappingResource, + const OFString &contextGroupKeyword, const OFString &contextGroupVersion, const OFString &contextGroupUID, const DSRCodedEntryValue &selectedValue) : Identifier(contextIdentifier), MappingResource(mappingResource), + Keyword(contextGroupKeyword), Version(contextGroupVersion), UID(contextGroupUID), SelectedValue(selectedValue), @@ -149,6 +151,7 @@ void DSRContextGroup::printHeader(STD_NAMESPACE ostream &stream) const /* output some general information on the context group */ stream << "CID " << getIdentifier() << OFendl; stream << " Resource : " << getMappingResource() << OFendl; + stream << " Keyword : " << getKeyword() << OFendl; stream << " Version : " << getVersion() << OFendl; stream << " UID : " << getUID() << OFendl; stream << " Type : " << (isExtensible() ? "extensible" : "non-extensible") << OFendl; diff --git a/dcmsr/libsrc/dsrdoc.cc b/dcmsr/libsrc/dsrdoc.cc index 30d74769..6dd15dde 100644 --- a/dcmsr/libsrc/dsrdoc.cc +++ b/dcmsr/libsrc/dsrdoc.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2022, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -82,6 +82,7 @@ DSRDocument::DSRDocument(const E_DocumentType documentType) IssuerOfPatientID(DCM_IssuerOfPatientID), PatientBirthDate(DCM_PatientBirthDate), PatientSex(DCM_PatientSex), + PatientAge(DCM_PatientAge), PatientSize(DCM_PatientSize), PatientWeight(DCM_PatientWeight), Manufacturer(DCM_Manufacturer), @@ -156,6 +157,7 @@ void DSRDocument::clear() IssuerOfPatientID.clear(); PatientBirthDate.clear(); PatientSex.clear(); + PatientAge.clear(); PatientSize.clear(); PatientWeight.clear(); Manufacturer.clear(); @@ -592,6 +594,8 @@ OFCondition DSRDocument::read(DcmItem &dataset, VerificationFlagEnum = enumeratedValueToVerificationFlag(getStringValueFromElement(VerificationFlag, tmpString)); if (VerificationFlagEnum == VF_invalid) printUnknownValueWarningMessage("VerificationFlag", tmpString.c_str()); + else if ((VerificationFlagEnum == VF_Unverified) && !VerifyingObserver.isEmpty()) + DCMSR_WARN("Verifying Observer(s) should not be present when Verification Flag is 'UNVERIFIED'"); else if (VerificationFlagEnum == VF_Verified) checkElementValue(VerifyingObserver, "1-n", "1", obsSearchCond, "SRDocumentGeneralModule"); } @@ -635,6 +639,7 @@ OFCondition DSRDocument::readStudyData(DcmItem &dataset, getAndCheckElementFromDataset(dataset, AccessionNumber, "1", "2", "GeneralStudyModule"); getAndCheckElementFromDataset(dataset, StudyDescription, "1", "3", "GeneralStudyModule"); // --- Patient Study Module --- + getAndCheckElementFromDataset(dataset, PatientAge, "1", "3", "PatientStudyModule"); getAndCheckElementFromDataset(dataset, PatientSize, "1", "3", "PatientStudyModule"); getAndCheckElementFromDataset(dataset, PatientWeight, "1", "3", "PatientStudyModule"); /* also read data from Patient Module */ @@ -695,6 +700,7 @@ OFCondition DSRDocument::write(DcmItem &dataset, addElementToDataset(result, dataset, new DcmCodeString(PatientSex), "1", "2", "PatientModule"); // --- Patient Study Module --- + addElementToDataset(result, dataset, new DcmAgeString(PatientAge), "1", "3", "PatientStudyModule"); addElementToDataset(result, dataset, new DcmDecimalString(PatientSize), "1", "3", "PatientStudyModule"); addElementToDataset(result, dataset, new DcmDecimalString(PatientWeight), "1", "3", "PatientStudyModule"); @@ -979,7 +985,8 @@ OFCondition DSRDocument::readXMLPatientData(const DSRXMLDocument &doc, else if (doc.getElementFromNodeContent(cursor, PatientID, "id").bad() && doc.getElementFromNodeContent(cursor, IssuerOfPatientID, "issuer").bad() && doc.getElementFromNodeContent(cursor, PatientSex, "sex").bad() && - /* strictly speaking, Patient's Size and Weight belong to the Study IE */ + /* strictly speaking, Patient's Age, Size and Weight belong to the Study IE */ + doc.getElementFromNodeContent(cursor, PatientAge, "age").bad() && doc.getElementFromNodeContent(cursor, PatientSize, "size").bad() && doc.getElementFromNodeContent(cursor, PatientWeight, "weight").bad()) { @@ -1185,7 +1192,11 @@ OFCondition DSRDocument::readXMLDocumentData(const DSRXMLDocument &doc, result = readXMLVerifyingObserverData(doc, cursor.getChild(), flags); /* allow absence in case of UNVERIFIED */ if (VerificationFlagEnum == VF_Unverified) + { + if (!VerifyingObserver.isEmpty()) + DCMSR_WARN("Verifying Observer(s) should not be present when Verification Flag is 'UNVERIFIED'"); result = EC_Normal; + } } else printUnknownValueWarningMessage("VerificationFlag", tmpString.c_str()); } @@ -1377,7 +1388,8 @@ OFCondition DSRDocument::writeXML(STD_NAMESPACE ostream &stream, stream << "" << OFendl; } writeStringFromElementToXML(stream, PatientSex, "sex", (flags & XF_writeEmptyTags) > 0); - /* strictly speaking, Patient's Size and Weight belong to the Study IE */ + /* strictly speaking, Patient's Age, Size and Weight belong to the Study IE */ + writeStringFromElementToXML(stream, PatientAge, "age", (flags & XF_writeEmptyTags) > 0); writeStringFromElementToXML(stream, PatientSize, "size", (flags & XF_writeEmptyTags) > 0); writeStringFromElementToXML(stream, PatientWeight, "weight", (flags & XF_writeEmptyTags) > 0); stream << "" << OFendl; @@ -2308,6 +2320,13 @@ OFCondition DSRDocument::getPatientSex(OFString &value, } +OFCondition DSRDocument::getPatientAge(OFString &value, + const signed long pos) const +{ + return getStringValueFromElement(PatientAge, value, pos); +} + + OFCondition DSRDocument::getPatientSize(OFString &value, const signed long pos) const { @@ -2572,6 +2591,16 @@ OFCondition DSRDocument::setPatientSex(const OFString &value, } +OFCondition DSRDocument::setPatientAge(const OFString &value, + const OFBool check) +{ + OFCondition result = (check) ? DcmAgeString::checkStringValue(value, "1") : EC_Normal; + if (result.good()) + result = PatientAge.putOFStringArray(value); + return result; +} + + OFCondition DSRDocument::setPatientSize(const OFString &value, const OFBool check) { @@ -2835,6 +2864,7 @@ void DSRDocument::createNewStudy() AccessionNumber.clear(); StudyDescription.clear(); /* also need to clear the attributes from the Patient Study Module */ + PatientAge.clear(); PatientSize.clear(); PatientWeight.clear(); /* the following method also creates new a study (since UID is empty) and SOP instance */ diff --git a/dcmsr/libsrc/dsrdoctn.cc b/dcmsr/libsrc/dsrdoctn.cc index 01dff74d..691f97bf 100644 --- a/dcmsr/libsrc/dsrdoctn.cc +++ b/dcmsr/libsrc/dsrdoctn.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -132,7 +132,7 @@ OFBool DSRDocumentTreeNode::isShort(const size_t /*flags*/) const OFBool DSRDocumentTreeNode::hasTemplateIdentification() const { - /* mapping resource UID is optional, so do not check it */ + /* Mapping Resource UID is optional, so do not check it */ return !TemplateIdentifier.empty() && !MappingResource.empty(); } @@ -253,9 +253,9 @@ OFCondition DSRDocumentTreeNode::readXML(const DSRXMLDocument &doc, DCMSR_WARN("Content item has invalid/incomplete template identification"); } } - /* read concept name (not required in some cases) */ + /* read Concept Name (not required in some cases) */ ConceptName.readXML(doc, doc.getNamedChildNode(cursor, "concept", OFFalse /*required*/), flags); - /* read observation UID and date/time (optional) */ + /* read Observation UID and date/time (optional) */ const DSRXMLCursor childCursor = doc.getNamedChildNode(cursor, "observation", OFFalse /*required*/); if (childCursor.valid()) { @@ -356,10 +356,10 @@ OFCondition DSRDocumentTreeNode::writeXML(STD_NAMESPACE ostream &stream, } } } - /* relationship type */ + /* Relationship Type */ if ((RelationshipType != RT_isRoot) && !(flags & XF_relationshipTypeAsAttribute)) writeStringValueToXML(stream, relationshipTypeToDefinedTerm(RelationshipType), "relationship", (flags & XF_writeEmptyTags) > 0); - /* concept name */ + /* Concept Name */ if (ConceptName.isValid()) { if (flags & XF_codeComponentsAsAttribute) @@ -371,7 +371,7 @@ OFCondition DSRDocumentTreeNode::writeXML(STD_NAMESPACE ostream &stream, } if (!(ObservationDateTime.empty() && ObservationUID.empty())) { - /* observation UID (optional) */ + /* Observation UID (optional) */ OFString tmpString; stream << "getRootTemplateIdentification(expectedTemplateIdentifier, expectedMappingResource); - /* read ContentTemplateSequence (conditional) */ + /* read Content Template Sequence (conditional) */ DcmItem *ditem = NULL; if (dataset.findAndGetSequenceItem(DCM_ContentTemplateSequence, ditem, 0 /*itemNum*/).good()) { @@ -763,6 +763,15 @@ OFCondition DSRDocumentTreeNode::readDocumentRelationshipMacro(DcmItem &dataset, } } } + else if (!MappingResource.empty()) + { + /* check whether an incorrect Mapping Resource UID is used */ + if (MappingResourceUID == UID_DICOMContentMappingResource) + { + DCMSR_WARN("Incorrect value for Mapping Resource UID (" << MappingResourceUID << "), " + << "should only be used for 'DCMR' and not for '" << MappingResource << "'"); + } + } /* check whether the expected template (if known) has been used */ if (!expectedTemplateIdentifier.empty()) { @@ -788,7 +797,7 @@ OFCondition DSRDocumentTreeNode::readDocumentRelationshipMacro(DcmItem &dataset, DCMSR_WARN("Content Template Sequence missing or empty, Template Identifier " << expectedTemplateIdentifier << " (" << expectedMappingResource << ") expected"); } - /* read ContentSequence */ + /* read Content Sequence */ if (result.good()) result = readContentSequence(dataset, constraintChecker, posString, flags); return result; @@ -810,12 +819,12 @@ OFCondition DSRDocumentTreeNode::writeDocumentRelationshipMacro(DcmItem &dataset /* add to marked items stack */ if (MarkFlag && (markedItems != NULL)) markedItems->push(&dataset); - /* write ObservationDateTime (conditional) */ + /* write Observation DateTime (conditional) */ result = putStringValueToDataset(dataset, DCM_ObservationDateTime, ObservationDateTime, OFFalse /*allowEmpty*/); - /* write ObservationUID (optional) */ + /* write Observation UID (optional) */ if (result.good()) result = putStringValueToDataset(dataset, DCM_ObservationUID, ObservationUID, OFFalse /*allowEmpty*/); - /* write ContentTemplateSequence (conditional) */ + /* write Content Template Sequence (conditional) */ if (result.good()) { if (hasTemplateIdentification()) @@ -846,9 +855,9 @@ OFCondition DSRDocumentTreeNode::readDocumentContentMacro(DcmItem &dataset, const size_t flags) { OFCondition result = EC_Normal; - /* skip reading ValueType, already done somewhere else */ + /* skip reading Value Type, already done somewhere else */ - /* read ConceptNameCodeSequence */ + /* read Concept Name Code Sequence */ if (RelationshipType == RT_isRoot) { /* the concept name is required for the root container */ @@ -861,7 +870,7 @@ OFCondition DSRDocumentTreeNode::readDocumentContentMacro(DcmItem &dataset, { if (result.bad()) DCMSR_DEBUG("Ignoring content item error because of read flag"); - /* read ContentItem (depending on ValueType) */ + /* read Content Item (depending on Value Type) */ result = readContentItem(dataset, flags); } /* check for validity, after reading */ @@ -894,9 +903,9 @@ OFCondition DSRDocumentTreeNode::readDocumentContentMacro(DcmItem &dataset, OFCondition DSRDocumentTreeNode::writeDocumentContentMacro(DcmItem &dataset) const { OFCondition result = EC_Normal; - /* write ValueType */ + /* write Value Type */ result = putStringValueToDataset(dataset, DCM_ValueType, valueTypeToDefinedTerm(ValueType)); - /* write ConceptNameCodeSequence */ + /* write Concept Name Code Sequence */ if (result.good()) { if (ConceptName.isValid()) @@ -907,7 +916,7 @@ OFCondition DSRDocumentTreeNode::writeDocumentContentMacro(DcmItem &dataset) con /* check for validity, before writing */ if (!isValid()) printInvalidContentItemMessage("Writing", this); - /* write ContentItem (depending on ValueType) */ + /* write Content Item (depending on Value Type) */ result = writeContentItem(dataset); } return result; @@ -964,7 +973,7 @@ OFCondition DSRDocumentTreeNode::readContentSequence(DcmItem &dataset, { OFCondition result = EC_Normal; DcmSequenceOfItems *dseq = NULL; - /* read ContentSequence (might be absent or empty) */ + /* read Content Sequence (might be absent or empty) */ if (dataset.findAndGetSequence(DCM_ContentSequence, dseq).good()) { OFString tmpString; @@ -987,7 +996,7 @@ OFCondition DSRDocumentTreeNode::readContentSequence(DcmItem &dataset, location += numberToString(OFstatic_cast(size_t, i + 1), buffer, sizeof(buffer)); if (flags & RF_showCurrentlyProcessedItem) DCMSR_INFO("Processing content item " << location); - /* read RelationshipType */ + /* read Relationship Type */ result = getAndCheckStringValueFromDataset(*ditem, DCM_RelationshipType, tmpString, "1", "1", "content item"); if (result.good() || (flags & RF_acceptUnknownRelationshipType)) { @@ -1005,14 +1014,14 @@ OFCondition DSRDocumentTreeNode::readContentSequence(DcmItem &dataset, { /* create new node (by-reference, no constraint checker required) */ result = createAndAppendNewNode(node, relationshipType, VT_byReference); - /* read ReferencedContentItemIdentifier (again) */ + /* read Referenced Content Item Identifier (again) */ if (result.good()) { newNode = node; result = node->readContentItem(*ditem, flags); } } else { - /* read ValueType (from DocumentContentMacro) - required to create new node */ + /* read Value Type (from Document Content Macro) - required to create new node */ result = getAndCheckStringValueFromDataset(*ditem, DCM_ValueType, tmpString, "1", "1", "content item"); if (result.good()) { @@ -1023,12 +1032,12 @@ OFCondition DSRDocumentTreeNode::readContentSequence(DcmItem &dataset, { /* create new node (by-value) */ result = createAndAppendNewNode(node, relationshipType, valueType, (flags & RF_ignoreRelationshipConstraints) ? NULL : constraintChecker); - /* read RelationshipMacro */ + /* read Relationship Macro */ if (result.good()) { newNode = node; result = node->readDocumentRelationshipMacro(*ditem, constraintChecker, location, flags); - /* read DocumentContentMacro */ + /* read Document Content Macro */ if (result.good()) result = node->readDocumentContentMacro(*ditem, location.c_str(), flags); } else { @@ -1079,7 +1088,7 @@ OFCondition DSRDocumentTreeNode::writeContentSequence(DcmItem &dataset, DSRDocumentTreeNodeCursor cursor(getDown()); if (cursor.isValid()) { - /* write ContentSequence */ + /* write Content Sequence */ DcmSequenceOfItems *dseq = new DcmSequenceOfItems(DCM_ContentSequence); if (dseq != NULL) { @@ -1091,19 +1100,19 @@ OFCondition DSRDocumentTreeNode::writeContentSequence(DcmItem &dataset, ditem = new DcmItem(); if (ditem != NULL) { - /* write RelationshipType */ + /* write Relationship Type */ result = putStringValueToDataset(*ditem, DCM_RelationshipType, relationshipTypeToDefinedTerm(node->getRelationshipType())); /* check for by-reference relationship */ if (node->getValueType() == VT_byReference) { - /* write ReferencedContentItemIdentifier */ + /* write Referenced Content Item Identifier */ if (result.good()) result = node->writeContentItem(*ditem); } else { // by-value - /* write RelationshipMacro */ + /* write Relationship Macro */ if (result.good()) result = node->writeDocumentRelationshipMacro(*ditem, markedItems); - /* write DocumentContentMacro */ + /* write Document Content Macro */ if (result.good()) node->writeDocumentContentMacro(*ditem); } @@ -1141,7 +1150,7 @@ OFCondition DSRDocumentTreeNode::renderHTMLConceptName(STD_NAMESPACE ostream &do if (!ConceptName.getCodeMeaning().empty()) { docStream << ""; - /* render ConceptName & Code (if valid) */ + /* render Concept Name & Code (if valid) */ ConceptName.renderHTML(docStream, flags, (flags & HF_renderConceptNameCodes) && ConceptName.isValid() /*fullCode*/); docStream << ":"; writeLine = OFTrue; @@ -1149,7 +1158,7 @@ OFCondition DSRDocumentTreeNode::renderHTMLConceptName(STD_NAMESPACE ostream &do else if (flags & HF_currentlyInsideAnnex) { docStream << ""; - /* render ValueType only */ + /* render Value Type only */ docStream << valueTypeToReadableName(ValueType); docStream << ":"; writeLine = OFTrue; diff --git a/dcmsr/libsrc/dsrimgvl.cc b/dcmsr/libsrc/dsrimgvl.cc index 50033558..00b548e2 100644 --- a/dcmsr/libsrc/dsrimgvl.cc +++ b/dcmsr/libsrc/dsrimgvl.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -89,9 +89,8 @@ DSRImageReferenceValue::DSRImageReferenceValue(const DSRImageReferenceValue &ref { /* do not check values since this would be unexpected to the user */ - /* create copy of icon image (if any), first frame only */ - if (referenceValue.IconImage != NULL) - IconImage = referenceValue.IconImage->createDicomImage(0 /*fstart*/, 1 /*fcount*/); + /* create copy of icon image (if any) */ + copyIconImage(referenceValue.IconImage); } @@ -124,8 +123,8 @@ DSRImageReferenceValue &DSRImageReferenceValue::operator=(const DSRImageReferenc SegmentList = referenceValue.SegmentList; PresentationState = referenceValue.PresentationState; RealWorldValueMapping = referenceValue.RealWorldValueMapping; - /* create copy of icon image (if any), first frame only */ - IconImage = (referenceValue.IconImage != NULL) ? referenceValue.IconImage->createDicomImage(0 /*fstart*/, 1 /*fcount*/) : NULL; + /* create copy of icon image (if any) */ + copyIconImage(referenceValue.IconImage); } return *this; } @@ -182,6 +181,12 @@ OFBool DSRImageReferenceValue::isSegmentation() const } +OFBool DSRImageReferenceValue::hasIconImage() const +{ + return (IconImage != NULL); +} + + OFCondition DSRImageReferenceValue::print(STD_NAMESPACE ostream &stream, const size_t flags) const { @@ -632,6 +637,8 @@ OFCondition DSRImageReferenceValue::setValue(const DSRImageReferenceValue &refer /* ignore status (return value) since the references are optional */ setPresentationState(referenceValue.PresentationState, check); setRealWorldValueMapping(referenceValue.RealWorldValueMapping, check); + /* create copy of icon image (if any) */ + copyIconImage(referenceValue.IconImage); } return result; } @@ -785,6 +792,18 @@ OFCondition DSRImageReferenceValue::checkCurrentValue(const OFBool reportWarning } +void DSRImageReferenceValue::copyIconImage(DicomImage *image) +{ + /* first, delete the current icon image */ + delete IconImage; + /* then create a copy of the given icon image (first frame only) */ + if (image != NULL) + IconImage = image->createDicomImage(0 /*fstart*/, 1 /*fcount*/); + else + IconImage = NULL; +} + + // comparison operators OFBool operator==(const DSRImageReferenceValue &lhs, diff --git a/dcmsr/libsrc/dsrnumvl.cc b/dcmsr/libsrc/dsrnumvl.cc index 37525c9d..9c21f933 100644 --- a/dcmsr/libsrc/dsrnumvl.cc +++ b/dcmsr/libsrc/dsrnumvl.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -238,11 +238,10 @@ OFCondition DSRNumericMeasurementValue::writeXML(STD_NAMESPACE ostream &stream, stream << ""; if (hasFloating) { - /* increase default precision */ - const STD_NAMESPACE streamsize oldPrecision = stream.precision(8); - stream << floatValue; - /* reset i/o manipulators */ - stream.precision(oldPrecision); + char buffer[64]; + /* need to convert float to avoid problems with decimal point and "-nan" */ + OFStandard::ftoa(buffer, sizeof(buffer), floatValue, 0, 0, -2 /* print enough digits to permit lossless conversion back to FD */); + stream << buffer; } stream << "" << OFendl; } diff --git a/dcmsr/libsrc/dsrtypes.cc b/dcmsr/libsrc/dsrtypes.cc index 1774c957..5b813915 100644 --- a/dcmsr/libsrc/dsrtypes.cc +++ b/dcmsr/libsrc/dsrtypes.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -400,7 +400,9 @@ static const S_PresentationStateTypeNameMap PresentationStateTypeNameMap[] = {DSRTypes::PT_VolumeRendering, UID_VolumeRenderingVolumetricPresentationStateStorage, "VR-VPS"}, {DSRTypes::PT_SegmentedVolumeRendering, UID_SegmentedVolumeRenderingVolumetricPresentationStateStorage, "SVR-VPS"}, {DSRTypes::PT_MultipleVolumeRendering, UID_MultipleVolumeRenderingVolumetricPresentationStateStorage, "MVR-VPS"}, - {DSRTypes::PT_VariableModalityLUT, UID_VariableModalityLUTSoftcopyPresentationStateStorage, "VML-SPS"} + {DSRTypes::PT_VariableModalityLUT, UID_VariableModalityLUTSoftcopyPresentationStateStorage, "VML-SPS"}, + {DSRTypes::PT_Waveform, UID_WaveformPresentationStateStorage, "WPS"}, + {DSRTypes::PT_WaveformAcquisition, UID_WaveformAcquisitionPresentationStateStorage, "WAPS"} }; diff --git a/dcmsr/libsrc/dsrxmld.cc b/dcmsr/libsrc/dsrxmld.cc index 74f2ad70..d157e61e 100644 --- a/dcmsr/libsrc/dsrxmld.cc +++ b/dcmsr/libsrc/dsrxmld.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2003-2024, OFFIS e.V. + * Copyright (C) 2003-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -33,18 +33,13 @@ #endif /* LIBXML_SCHEMAS_ENABLED */ // This function is also used in xml2dcm, try to stay in sync! -#if defined(HAVE_VSNPRINTF) && defined(HAVE_PROTOTYPE_VSNPRINTF) extern "C" void errorFunction(void * ctx, const char *msg, ...) -#else -extern "C" void errorFunction(void * /* ctx */, const char *msg, ...) -#endif { OFLogger xmlLogger = OFLog::getLogger("dcmtk.dcmsr.libxml"); if (!xmlLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL)) return; -#if defined(HAVE_VSNPRINTF) && defined(HAVE_PROTOTYPE_VSNPRINTF) // libxml calls us multiple times for one line of log output which would // result in garbled output. To avoid this, we buffer the output in a local // string in the caller which we get through our 'ctx' parameter. Then, we @@ -76,23 +71,6 @@ extern "C" void errorFunction(void * /* ctx */, const char *msg, ...) pos = buffer.find('\n'); } -#else - // No vsnprint, but at least vfprintf. Output the messages directly to stderr. - va_list ap; - va_start(ap, msg); -#ifdef HAVE_PROTOTYPE_STD__VFPRINTF - std::vfprintf(stderr, msg, ap); -#else - vfprintf(stderr, msg, ap); -#endif - va_end(ap); -#endif - -#ifndef HAVE_VSNPRINTF - // Only the vsnprintf() branch above uses 'buffer' which means the compiler - // would warn about an unused variable if HAVE_VSNPRINTF is undefined. - buffer += ""; -#endif } #endif /* WITH_LIBXML */ diff --git a/dcmsr/tests/CMakeLists.txt b/dcmsr/tests/CMakeLists.txt index f4ecd4cc..26f043c8 100644 --- a/dcmsr/tests/CMakeLists.txt +++ b/dcmsr/tests/CMakeLists.txt @@ -7,6 +7,7 @@ DCMTK_ADD_TEST_EXECUTABLE(dcmsr_tests tsrdoc.cc tsrdoctr.cc tsrlist.cc + tsrimgvl.cc tsrnumvl.cc tsrtpl.cc tsrtree.cc diff --git a/dcmsr/tests/Makefile.dep b/dcmsr/tests/Makefile.dep index 7749800c..c8103c69 100644 --- a/dcmsr/tests/Makefile.dep +++ b/dcmsr/tests/Makefile.dep @@ -87,8 +87,9 @@ mkreport.o: mkreport.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmsr/dsrwavch.h ../include/dcmtk/dcmsr/dsrrtpl.h \ ../include/dcmtk/dcmsr/dsrctpl.h ../include/dcmtk/dcmsr/dsrsoprf.h \ ../include/dcmtk/dcmsr/dsrrefin.h ../include/dcmtk/dcmsr/dsrcsidl.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrda.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ @@ -118,7 +119,6 @@ mkreport.o: mkreport.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmdata/include/dcmtk/dcmdata/dcpixseq.h \ ../../dcmdata/include/dcmtk/dcmdata/dcofsetl.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrae.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrdt.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrur.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ @@ -290,8 +290,9 @@ tsrcmr.o: tsrcmr.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmsr/dsrwavch.h ../include/dcmtk/dcmsr/dsrrtpl.h \ ../include/dcmtk/dcmsr/dsrctpl.h ../include/dcmtk/dcmsr/dsrsoprf.h \ ../include/dcmtk/dcmsr/dsrrefin.h ../include/dcmtk/dcmsr/dsrcsidl.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrda.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ @@ -506,8 +507,9 @@ tsrdoc.o: tsrdoc.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmsr/dsrwavch.h ../include/dcmtk/dcmsr/dsrrtpl.h \ ../include/dcmtk/dcmsr/dsrctpl.h ../include/dcmtk/dcmsr/dsrsoprf.h \ ../include/dcmtk/dcmsr/dsrrefin.h ../include/dcmtk/dcmsr/dsrcsidl.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrda.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ @@ -611,8 +613,9 @@ tsrdoctr.o: tsrdoctr.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmsr/dsrwavch.h ../include/dcmtk/dcmsr/dsrrtpl.h \ ../include/dcmtk/dcmsr/dsrctpl.h ../include/dcmtk/dcmsr/dsrsoprf.h \ ../include/dcmtk/dcmsr/dsrrefin.h ../include/dcmtk/dcmsr/dsrcsidl.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrda.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ @@ -626,6 +629,85 @@ tsrdoctr.o: tsrdoctr.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmsr/dsrreftn.h ../include/dcmtk/dcmsr/dsrimgtn.h \ ../include/dcmtk/dcmsr/dsrnumtn.h ../include/dcmtk/dcmsr/dsrtextn.h \ ../include/dcmtk/dcmsr/dsrstrvl.h +tsrimgvl.o: tsrimgvl.cc ../../config/include/dcmtk/config/osconfig.h \ + ../../ofstd/include/dcmtk/ofstd/oftest.h \ + ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ + ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../ofstd/include/dcmtk/ofstd/ofexit.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcuid.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ + ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ + ../include/dcmtk/dcmsr/dsrimgvl.h ../include/dcmtk/dcmsr/dsrtypes.h \ + ../include/dcmtk/dcmsr/dsdefine.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ + ../include/dcmtk/dcmsr/dsrcomvl.h ../include/dcmtk/dcmsr/dsrimgfr.h \ + ../include/dcmtk/dcmsr/dsrtlist.h ../include/dcmtk/dcmsr/dsrimgse.h \ + ../include/dcmtk/dcmsr/dsrimgtn.h ../include/dcmtk/dcmsr/dsrdoctn.h \ + ../include/dcmtk/dcmsr/dsrtree.h ../include/dcmtk/dcmsr/dsrtncsr.h \ + ../include/dcmtk/dcmsr/dsrposcn.h ../include/dcmtk/dcmsr/dsrtnant.h \ + ../../ofstd/include/dcmtk/ofstd/ofstack.h \ + ../include/dcmtk/dcmsr/dsrcodvl.h ../include/dcmtk/dcmsr/codes/dcm.h tsrlist.o: tsrlist.cc ../../config/include/dcmtk/config/osconfig.h \ ../../ofstd/include/dcmtk/ofstd/oftest.h \ ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ @@ -849,8 +931,9 @@ tsrtpl.o: tsrtpl.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/dcmsr/dsrwavch.h ../include/dcmtk/dcmsr/dsrrtpl.h \ ../include/dcmtk/dcmsr/dsrctpl.h ../include/dcmtk/dcmsr/dsrsoprf.h \ ../include/dcmtk/dcmsr/dsrrefin.h ../include/dcmtk/dcmsr/dsrcsidl.h \ - ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvras.h \ ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrda.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvrds.h \ ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ diff --git a/dcmsr/tests/Makefile.in b/dcmsr/tests/Makefile.in index f22d07cf..d0796ec7 100644 --- a/dcmsr/tests/Makefile.in +++ b/dcmsr/tests/Makefile.in @@ -27,8 +27,8 @@ LIBDIRS = -L$(top_srcdir)/libcmr -L$(top_srcdir)/libsrc -L$(ofstddir)/libsrc \ LOCALLIBS = -lcmr -ldcmsr -ldcmimage -ldcmimgle -ldcmdata -loflog -lofstd -loficonv \ $(TIFFLIBS) $(PNGLIBS) $(XMLLIBS) $(ZLIBLIBS) $(CHARCONVLIBS) $(MATHLIBS) -tstobjs = tests.o tsrtree.o tsrdoctr.o tsrdoc.o tsrcodvl.o tsrnumvl.o tsrtpl.o \ - tsrcmr.o tsrlist.o +tstobjs = tests.o tsrtree.o tsrdoctr.o tsrdoc.o tsrcodvl.o tsrimgvl.o tsrnumvl.o \ + tsrtpl.o tsrcmr.o tsrlist.o objs = mkreport.o $(tstobjs) progs = mkreport tests diff --git a/dcmsr/tests/tests.cc b/dcmsr/tests/tests.cc index 1513148a..a79bb98d 100644 --- a/dcmsr/tests/tests.cc +++ b/dcmsr/tests/tests.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2012-2022, OFFIS e.V. + * Copyright (C) 2012-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -83,6 +83,8 @@ OFTEST_REGISTER(dcmsr_determineCodeValueType); OFTEST_REGISTER(dcmsr_writeCodeSequence); OFTEST_REGISTER(dcmsr_compareCodedEntry); OFTEST_REGISTER(dcmsr_useBasicCodedEntry); +OFTEST_REGISTER(dcmsr_createMonochromeIconImage); +OFTEST_REGISTER(dcmsr_createColorIconImage); OFTEST_REGISTER(dcmsr_setNumericMeasurementValue); OFTEST_REGISTER(dcmsr_emptyMeasurementValueSequence); OFTEST_REGISTER(dcmsr_setAndGetFloatingPointRepresentation); diff --git a/dcmsr/tests/tsrcmr.cc b/dcmsr/tests/tsrcmr.cc index 0b26f46f..8d619ce5 100644 --- a/dcmsr/tests/tsrcmr.cc +++ b/dcmsr/tests/tsrcmr.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2022, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2015-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -66,6 +66,8 @@ OFTEST(dcmsr_CID29e_AcquisitionModality) OFCHECK(ctxGroup.mapModality("XYZ").isEmpty()); OFCHECK(ctxGroup.mapModality("ABC", codedEntry) == SR_EC_UnsupportedValue); OFCHECK(ctxGroup.selectValue("").bad()); + /* also check the keyword of the context group */ + OFCHECK_EQUAL(ctxGroup.getKeyword(), "AcquisitionModality"); } @@ -81,6 +83,8 @@ OFTEST(dcmsr_CID42_NumericValueQualifier) OFCHECK(ctxGroup.findCodedEntry(DSRBasicCodedEntry("", "99TEST", "Some invalid test code")).bad()); OFCHECK(ctxGroup.findCodedEntry(DSRBasicCodedEntry("0815", "99TEST", "-")).good()); OFCHECK(ctxGroup.findCodedEntry(DSRBasicCodedEntry("", "", "")).bad()); + /* also check the keyword of the context group */ + OFCHECK_EQUAL(ctxGroup.getKeyword(), "NumericValueQualifier"); } @@ -97,6 +101,8 @@ OFTEST(dcmsr_CID244e_Laterality) OFCHECK(!ctxGroup.mapImageLaterality("XYZ").isValid()); OFCHECK(ctxGroup.mapImageLaterality("ABC", codedEntry) == SR_EC_InvalidValue); OFCHECK(ctxGroup.selectValue("").bad()); + /* also check the keyword of the context group */ + OFCHECK_EQUAL(ctxGroup.getKeyword(), "Laterality"); } diff --git a/dcmsr/tests/tsrdoc.cc b/dcmsr/tests/tsrdoc.cc index f8c76bc8..e96f7302 100644 --- a/dcmsr/tests/tsrdoc.cc +++ b/dcmsr/tests/tsrdoc.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2014-2021, J. Riesmeier, Oldenburg, Germany + * Copyright (C) 2014-2025, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -49,6 +49,9 @@ OFTEST(dcmsr_setAndGetPatientData) OFCHECK(doc.getPatientSex(value).good()); OFCHECK_EQUAL(value, "M"); /* also check some recently introduced attributes */ + OFCHECK(doc.setPatientAge("042Y").good()); + OFCHECK(doc.getPatientAge(value).good()); + OFCHECK_EQUAL(value, "042Y"); OFCHECK(doc.setPatientSize("1.88").good()); OFCHECK(doc.getPatientSize(value).good()); OFCHECK_EQUAL(value, "1.88"); diff --git a/dcmsr/tests/tsrimgvl.cc b/dcmsr/tests/tsrimgvl.cc new file mode 100644 index 00000000..ae14cb09 --- /dev/null +++ b/dcmsr/tests/tsrimgvl.cc @@ -0,0 +1,122 @@ +/* + * + * Copyright (C) 2025, J. Riesmeier, Oldenburg, Germany + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation are maintained by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmsr + * + * Author: Joerg Riesmeier + * + * Purpose: + * test program for class DSRImageReferenceValue + * + */ + + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ + +#include "dcmtk/ofstd/oftest.h" + +#include "dcmtk/dcmdata/dcuid.h" +#include "dcmtk/dcmdata/dcdatset.h" +#include "dcmtk/dcmdata/dcdeftag.h" +#include "dcmtk/dcmdata/dctypes.h" + +#include "dcmtk/dcmsr/dsrimgvl.h" +#include "dcmtk/dcmsr/dsrimgtn.h" +#include "dcmtk/dcmsr/codes/dcm.h" + + +OFTEST(dcmsr_createMonochromeIconImage) +{ + DSRImageReferenceValue imgValue(UID_CTImageStorage, "1.2.3.4.5.6.7.8.9.0.98"); + OFCHECK(!imgValue.hasIconImage()); + + /* create a monochrome image dataset */ + DcmDataset dataset; + Uint8 pixelData[256]; + for (int i = 0; i < 256; i++) + pixelData[i] = OFstatic_cast(Uint8, i); + OFCHECK(dataset.putAndInsertString(DCM_PhotometricInterpretation, "MONOCHROME2").good()); + OFCHECK(dataset.putAndInsertUint16(DCM_SamplesPerPixel, 1).good()); + OFCHECK(dataset.putAndInsertUint16(DCM_PixelRepresentation, 0).good()); + OFCHECK(dataset.putAndInsertUint16(DCM_Rows, 16).good()); + OFCHECK(dataset.putAndInsertUint16(DCM_Columns, 16).good()); + OFCHECK(dataset.putAndInsertUint16(DCM_BitsAllocated, 8).good()); + OFCHECK(dataset.putAndInsertUint16(DCM_BitsStored, 8).good()); + OFCHECK(dataset.putAndInsertUint16(DCM_HighBit, 7).good()); + OFCHECK(dataset.putAndInsertUint8Array(DCM_PixelData, pixelData, sizeof(pixelData)).good()); + + /* create an icon image with 64x64 pixels */ + OFCHECK(imgValue.createIconImage(&dataset, EXS_Unknown /*xfer*/, 0 /*frame*/, 64 /*width*/, 64 /*height*/).good()); + OFCHECK(imgValue.hasIconImage()); + + /* set value to image tree node and write to item */ + DcmItem item; + DSRImageTreeNode imgNode(DSRTypes::RT_contains); + OFCHECK(imgNode.setConceptName(CODE_DCM_BestInSet).good()); + OFCHECK(imgNode.setValue(imgValue).good()); + OFCHECK(imgNode.hasIconImage()); + OFCHECK(imgNode.write(item).good()); + /* output content of the item (in debug mode only) */ + if (DCM_dcmsrLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL)) + item.print(COUT, DCMTypes::PF_shortenLongTagValues /*flags*/, 2 /*level*/); + + /* delete the icon image */ + imgNode.deleteIconImage(); + OFCHECK(!imgNode.hasIconImage()); +} + + +OFTEST(dcmsr_createColorIconImage) +{ + DSRImageReferenceValue imgValue(UID_CTImageStorage, "1.2.3.4.5.6.7.8.9.0.99"); + OFCHECK(!imgValue.hasIconImage()); + + /* create a color image dataset */ + DcmDataset dataset; + Uint8 pixelData[256][3]; + for (int i = 0; i < 256; i++) + { + pixelData[i][0] = OFstatic_cast(Uint8, i); + pixelData[i][1] = OFstatic_cast(Uint8, i); + pixelData[i][2] = OFstatic_cast(Uint8, i); + } + OFCHECK(dataset.putAndInsertString(DCM_PhotometricInterpretation, "RGB").good()); + OFCHECK(dataset.putAndInsertUint16(DCM_SamplesPerPixel, 3).good()); + OFCHECK(dataset.putAndInsertUint16(DCM_PixelRepresentation, 0).good()); + OFCHECK(dataset.putAndInsertUint16(DCM_PlanarConfiguration, 0).good()); + OFCHECK(dataset.putAndInsertUint16(DCM_Rows, 16).good()); + OFCHECK(dataset.putAndInsertUint16(DCM_Columns, 16).good()); + OFCHECK(dataset.putAndInsertUint16(DCM_BitsAllocated, 8).good()); + OFCHECK(dataset.putAndInsertUint16(DCM_BitsStored, 8).good()); + OFCHECK(dataset.putAndInsertUint16(DCM_HighBit, 7).good()); + OFCHECK(dataset.putAndInsertUint8Array(DCM_PixelData, pixelData[0], sizeof(pixelData)).good()); + + /* create an icon image with 64x64 pixels */ + OFCHECK(imgValue.createIconImage(&dataset, EXS_Unknown /*xfer*/, 0 /*frame*/, 64 /*width*/, 64 /*height*/).good()); + OFCHECK(imgValue.hasIconImage()); + + /* set value to image tree node and write to item */ + DcmItem item; + DSRImageTreeNode imgNode(DSRTypes::RT_hasProperties); + OFCHECK(imgNode.setConceptName(CODE_DCM_BestIllustrationOfFinding).good()); + OFCHECK(imgNode.setValue(imgValue).good()); + OFCHECK(imgNode.hasIconImage()); + OFCHECK(imgNode.write(item).good()); + /* output content of the item (in debug mode only) */ + if (DCM_dcmsrLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL)) + item.print(COUT, DCMTypes::PF_shortenLongTagValues /*flags*/, 2 /*level*/); + + /* delete the icon image */ + imgNode.deleteIconImage(); + OFCHECK(!imgNode.hasIconImage()); +} diff --git a/dcmtls/include/dcmtk/dcmtls/tlsscu.h b/dcmtls/include/dcmtk/dcmtls/tlsscu.h index 32fbda46..be442c18 100644 --- a/dcmtls/include/dcmtk/dcmtls/tlsscu.h +++ b/dcmtls/include/dcmtk/dcmtls/tlsscu.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2010-2023, OFFIS e.V. + * Copyright (C) 2010-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -118,6 +118,12 @@ public: */ virtual OFCondition setTLSProfile(DcmTLSSecurityProfile profile); + /** activate the ciphersuites that have been added to the list of ciphersuites + * for TLS negotiation. Caller must ensure that initNetwork() is executed before this call. + * @return EC_Normal if successful, an error code otherwise + */ + virtual OFCondition activateCipherSuites(); + /** adds a ciphersuite to the list of ciphersuites for TLS negotiation. * Caller must ensure that initNetwork() is executed before this call. * It is the responsibility of the user to ensure that the added ciphersuite diff --git a/dcmtls/libsrc/tlsciphr.cc b/dcmtls/libsrc/tlsciphr.cc index 61d381af..8f08b57f 100644 --- a/dcmtls/libsrc/tlsciphr.cc +++ b/dcmtls/libsrc/tlsciphr.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2018-2024, OFFIS e.V. + * Copyright (C) 2018-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -180,7 +180,7 @@ static const DcmCipherSuiteList globalTLS13CipherSuiteList[] = {"TLS_AES_256_GCM_SHA384", TLS1_3_RFC_AES_256_GCM_SHA384, TPV_TLSv13, TKE_TLSv13, TCA_TLSv13, TCE_AES, TCM_SHA384, TKM_GCM, 256, 256}, }; -#define GLOBAL_NUM_TLS13_CIPHERSUITES (sizeof(globalCipherSuiteList)/sizeof(DcmCipherSuiteList)) +#define GLOBAL_NUM_TLS13_CIPHERSUITES (sizeof(globalTLS13CipherSuiteList)/sizeof(DcmCipherSuiteList)) const size_t DcmTLSCiphersuiteHandler::unknownCipherSuiteIndex = (size_t) -1; diff --git a/dcmtls/libsrc/tlsfmacr.h b/dcmtls/libsrc/tlsfmacr.h index 80fbd850..c3424b72 100644 --- a/dcmtls/libsrc/tlsfmacr.h +++ b/dcmtls/libsrc/tlsfmacr.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2018-2024, OFFIS e.V. + * Copyright (C) 2018-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -32,7 +32,7 @@ BEGIN_EXTERN_C #include END_EXTERN_C -/* check if we fulfil all requirements for implementing the +/* check if we fulfil all requirements for implementing the * Modified BCP 195 RFC 8996 TLS Profile. With DICOM CP 2311 * making support for Camellia in GCM mode optional, this is now rather simple. */ diff --git a/dcmtls/libsrc/tlsscu.cc b/dcmtls/libsrc/tlsscu.cc index a9aff68f..5b84a5de 100644 --- a/dcmtls/libsrc/tlsscu.cc +++ b/dcmtls/libsrc/tlsscu.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2010-2023, OFFIS e.V. + * Copyright (C) 2010-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -268,6 +268,15 @@ OFCondition DcmTLSSCU::setTLSProfile(DcmTLSSecurityProfile profile) } else return EC_IllegalCall; } +OFCondition DcmTLSSCU::activateCipherSuites() +{ + if (m_tLayer) + { + return m_tLayer->activateCipherSuites(); + } + return EC_IllegalCall; +} + void DcmTLSSCU::setReadSeedFile(const OFString& seedFile) { m_readSeedFile = seedFile; diff --git a/dcmtls/libsrc/tlstrans.cc b/dcmtls/libsrc/tlstrans.cc index 92f37f79..19c04943 100644 --- a/dcmtls/libsrc/tlstrans.cc +++ b/dcmtls/libsrc/tlstrans.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1998-2024, OFFIS e.V. + * Copyright (C) 1998-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -34,9 +34,7 @@ BEGIN_EXTERN_C #ifdef HAVE_SYS_TIME_H #include #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_SELECT_H #include #endif diff --git a/dcmtls/tests/CMakeLists.txt b/dcmtls/tests/CMakeLists.txt index 6e9b80fd..95248b21 100644 --- a/dcmtls/tests/CMakeLists.txt +++ b/dcmtls/tests/CMakeLists.txt @@ -2,7 +2,7 @@ DCMTK_ADD_TEST_EXECUTABLE(dcmtls_tests tests.cc tscuscptls.cc) # make sure executables are linked to the corresponding libraries -DCMTK_TARGET_LINK_MODULES(dcmtls_tests dcmnet dcmtls) +DCMTK_TARGET_LINK_MODULES(dcmtls_tests dcmtls) # This macro parses tests.cc and registers all tests DCMTK_ADD_TESTS(dcmtls) diff --git a/dcmtls/tests/tscuscptls.cc b/dcmtls/tests/tscuscptls.cc index 119c6a1a..9017c37e 100644 --- a/dcmtls/tests/tscuscptls.cc +++ b/dcmtls/tests/tscuscptls.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2019-2023, OFFIS e.V. + * Copyright (C) 2019-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -43,26 +43,7 @@ return; \ } while (0) -/** Method that ensures that the current thread is actually sleeping for the - * defined number of seconds (at least). - * The problem with the regular sleep() function called from OFStandard::sleep - * is that it might be interrupted by signals or a network timeout (depending - * on the operating system). This methods re-executes OFStandard's sleep method - * until the desired number of seconds have elapsed. - * @param sleep The number of seconds to sleep (at least) - */ -static void force_sleep(Uint32 sleep) -{ - OFTimer timer; - double elapsed = timer.getDiff(); - while (elapsed < (double)sleep) - { - // Use ceiling since otherwise we could wait too short - OFStandard::sleep(OFstatic_cast(unsigned int, ceil(sleep-elapsed))); - elapsed = timer.getDiff(); - } -} - +const size_t NUM_THREADS = 20; /** TestSCP derived from DcmSCP in order to test TLS functionality * through function setTransportLayer of DcmSCPConfig. @@ -151,6 +132,7 @@ protected: m_is_running = OFTrue; m_listen_result = listen(); m_is_running = OFFalse; + DCMNET_DEBUG("TestPool: SCP Pool thread stopped, listen() returned: " << m_listen_result.text()); } }; @@ -347,7 +329,7 @@ OFTEST_FLAGS(dcmtls_scp_tls, EF_None) port_number = 0xF000 + (rnd.getRND16() & 0xFFF); config.setPort(port_number); scp.start(); - force_sleep(2); // wait 2 seconds for the SCP process to start + OFStandard::forceSleep(2); // wait 2 seconds for the SCP process to start memory_barrier.lock(); memory_barrier.unlock(); } @@ -417,26 +399,23 @@ OFTEST_FLAGS(dcmtls_scp_pool_tls, EF_None) xfers.push_back(UID_LittleEndianImplicitTransferSyntax); OFCHECK(config.addPresentationContext(UID_VerificationSOPClass, xfers, ASC_SC_ROLE_DEFAULT).good()); config.setTransportLayer(&scpTlsLayer); - pool.setMaxThreads(20); + pool.setMaxThreads(NUM_THREADS); // Ensure server is up and listening int i = 0; Uint16 port_number = 0; - OFMutex memory_barrier; do { // generate a random port number between 61440 (0xF000) and 65535 port_number = 0xF000 + (rnd.getRND16() & 0xFFF); config.setPort(port_number); pool.start(); - force_sleep(2); // wait 2 seconds for the SCP process to start - memory_barrier.lock(); - memory_barrier.unlock(); + OFStandard::forceSleep(2); // wait 2 seconds for the SCP process to start } while ((i++ < 5) && (! pool.m_is_running)); // try up to 5 port numbers before giving up - if (! pool.m_is_running) BAILOUT("Start of the SCP thread ppol failed: " << pool.m_listen_result.text()); + if (! pool.m_is_running) BAILOUT("Start of the SCP thread pool failed: " << pool.m_listen_result.text()); - OFVector scus(20); + OFVector scus(NUM_THREADS); OFVector scuTlsLayers; for (OFVector::iterator it1 = scus.begin(); it1 != scus.end(); ++it1) { @@ -460,7 +439,7 @@ OFTEST_FLAGS(dcmtls_scp_pool_tls, EF_None) // "ensure" the pool is initialized before any SCU starts connecting to it. The initialization // can take a couple of seconds on older systems, e.g. debian i368. - force_sleep(5); + OFStandard::forceSleep(5); for (OFVector::const_iterator it2 = scus.begin(); it2 != scus.end(); ++it2) { diff --git a/dcmtract/CMakeLists.txt b/dcmtract/CMakeLists.txt index 504b5209..5fa159bf 100644 --- a/dcmtract/CMakeLists.txt +++ b/dcmtract/CMakeLists.txt @@ -5,6 +5,6 @@ project(dcmtract) include_directories("${dcmtract_SOURCE_DIR}/include" "${dcmiod_SOURCE_DIR}/include" "${dcmdata_SOURCE_DIR}/include" "${ofstd_SOURCE_DIR}/include" "${oflog_SOURCE_DIR}/include" ${ZLIB_INCDIR}) # recurse into subdirectories -foreach(SUBDIR libsrc include) +foreach(SUBDIR libsrc include tests) add_subdirectory(${SUBDIR}) endforeach() diff --git a/dcmtract/include/dcmtk/dcmtract/trctrackset.h b/dcmtract/include/dcmtk/dcmtract/trctrackset.h index 37ed3309..3cf51b36 100644 --- a/dcmtract/include/dcmtk/dcmtract/trctrackset.h +++ b/dcmtract/include/dcmtk/dcmtract/trctrackset.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2016-2022, Open Connections GmbH + * Copyright (C) 2016-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -260,6 +260,7 @@ public: * (must be either 0, 1 or numPoints) * @param result Returns the resulting Track if creation was successful, * error otherwise + * @return EC_Normal if successful, error code otherwise */ virtual OFCondition addTrack(const Float32* pointData, const size_t numPoints, diff --git a/dcmtract/include/dcmtk/dcmtract/trctypes.h b/dcmtract/include/dcmtk/dcmtract/trctypes.h index 809922ac..4eaa67fc 100644 --- a/dcmtract/include/dcmtk/dcmtract/trctypes.h +++ b/dcmtract/include/dcmtk/dcmtract/trctypes.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2016-2024, Open Connections GmbH + * Copyright (C) 2016-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -66,6 +66,8 @@ extern DCMTK_DCMTRACT_EXPORT const OFConditionConst TRC_EC_MeasurementData extern DCMTK_DCMTRACT_EXPORT const OFConditionConst TRC_EC_InvalidStatisticData; /// Invalid Track Data extern DCMTK_DCMTRACT_EXPORT const OFConditionConst TRC_EC_InvalidTrackData; +/// Invalid Content Identification +extern DCMTK_DCMTRACT_EXPORT const OFConditionConst TRC_EC_InvalidContentIdentification; /** * Types specific to this module diff --git a/dcmtract/libsrc/Makefile.dep b/dcmtract/libsrc/Makefile.dep index 3844a534..924ec9cd 100644 --- a/dcmtract/libsrc/Makefile.dep +++ b/dcmtract/libsrc/Makefile.dep @@ -65,6 +65,7 @@ trcmeasurement.o: trcmeasurement.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h \ @@ -148,6 +149,7 @@ trcmodtractresults.o: trcmodtractresults.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h \ @@ -236,6 +238,7 @@ trcstatistic.o: trcstatistic.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h \ @@ -316,6 +319,7 @@ trctrack.o: trctrack.cc ../../config/include/dcmtk/config/osconfig.h \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../include/dcmtk/dcmtract/trctypes.h ../include/dcmtk/dcmtract/trcdef.h \ ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ @@ -390,6 +394,7 @@ trctrackset.o: trctrackset.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h \ @@ -475,6 +480,7 @@ trctractographyresults.o: trctractographyresults.cc \ ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ ../../ofstd/include/dcmtk/ofstd/ofmap.h \ ../../ofstd/include/dcmtk/ofstd/ofdate.h \ ../../ofstd/include/dcmtk/ofstd/oftime.h \ diff --git a/dcmtract/libsrc/trcmodtractresults.cc b/dcmtract/libsrc/trcmodtractresults.cc index 45e43cda..2e2e2376 100644 --- a/dcmtract/libsrc/trcmodtractresults.cc +++ b/dcmtract/libsrc/trcmodtractresults.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2016-2024, Open Connections GmbH + * Copyright (C) 2016-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -110,7 +110,7 @@ void TrcTractographyResultsModule::resetRules() m_Rules->addRule(new IODRule(DCM_ContentTime, "1", "1", getName(), DcmIODTypes::IE_INSTANCE), OFTrue); m_Rules->addRule(new IODRule(DCM_ContentDate, "1", "1", getName(), DcmIODTypes::IE_INSTANCE), OFTrue); m_Rules->addRule(new IODRule(DCM_TrackSetSequence, "1-n", "1", getName(), DcmIODTypes::IE_INSTANCE), OFTrue); - m_Rules->addRule(new IODRule(DCM_ReferencedInstanceSequence, "1-n", "1", getName(), DcmIODTypes::IE_INSTANCE), OFTrue); + m_Rules->addRule(new IODRule(DCM_ReferencedInstanceSequence, "1-n", "1C", getName(), DcmIODTypes::IE_INSTANCE), OFTrue); } @@ -193,13 +193,19 @@ OFCondition TrcTractographyResultsModule::addImageReference(const IODReference& OFCondition TrcTractographyResultsModule::check(const OFBool quiet) { + DCMTRACT_DEBUG("Checking Tractography Results Module"); + DCMTRACT_DEBUG("Checking Content Identification"); if (m_ContentIdentification.check().good()) { + DCMTRACT_DEBUG("Content Identification is valid"); + DCMTRACT_DEBUG("Checking Track Sets"); if (m_TrackSets.size() > 0) { + DCMTRACT_DEBUG("Found " << m_TrackSets.size() << " Track Sets"); OFVector::iterator it = m_TrackSets.begin(); while (it != m_TrackSets.end()) { + DCMTRACT_DEBUG("Checking Track Set #" << (it - m_TrackSets.begin() + 1) << "/" << m_TrackSets.size()); if ( (*it)->getNumberOfTracks() == 0) { DCMTRACT_ERROR("Track Set does not contain any tracks"); @@ -207,6 +213,7 @@ OFCondition TrcTractographyResultsModule::check(const OFBool quiet) } else { + DCMTRACT_DEBUG("Track Set contains " << (*it)->getNumberOfTracks() << " tracks, checking them"); OFVector tracks = (*it)->getTracks(); OFVector::iterator t = tracks.begin(); while (t != tracks.end()) @@ -224,16 +231,23 @@ OFCondition TrcTractographyResultsModule::check(const OFBool quiet) return TRC_EC_NoSuchTrack; } } + else + { + DCMTRACT_ERROR("Content Identification is not valid"); + return TRC_EC_InvalidContentIdentification; + } return checkColoring(); } OFCondition TrcTractographyResultsModule::checkColoring() { + DCMTRACT_DEBUG("Checking Track Set coloring"); size_t tsCount = 1; OFVector::iterator ts = m_TrackSets.begin(); while (ts != m_TrackSets.end()) { + DCMTRACT_DEBUG("Checking Track Set coloring for Track Set #" << tsCount << "/" << m_TrackSets.size()); // Collect statistics how much are colored OFVector::const_iterator track = (*ts)->getTracks().begin(); OFVector::const_iterator last = (*ts)->getTracks().end(); diff --git a/dcmtract/libsrc/trctrackset.cc b/dcmtract/libsrc/trctrackset.cc index c01230e8..2091ee77 100644 --- a/dcmtract/libsrc/trctrackset.cc +++ b/dcmtract/libsrc/trctrackset.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2016-2024, Open Connections GmbH + * Copyright (C) 2016-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -141,16 +141,29 @@ OFCondition TrcTrackSet::read(DcmItem& source, OFCondition TrcTrackSet::write(DcmItem& destination) { + DCMTRACT_DEBUG("Writing Track Set"); OFCondition result; + DCMTRACT_DEBUG("Writing Track Set Anatomical Type Code Sequence"); DcmIODUtil::writeSingleItem(result, DCM_TrackSetAnatomicalTypeCodeSequence, m_Anatomy, getData(), getRules()->getByTag(DCM_TrackSetAnatomicalTypeCodeSequence)); + DCMTRACT_DEBUG("Writing Diffusion Acquisition Code Sequence"); DcmIODUtil::writeSingleItem(result, DCM_DiffusionAcquisitionCodeSequence, m_DiffusionAcquisitionCode, getData(), getRules()->getByTag(DCM_DiffusionAcquisitionCodeSequence)); + DCMTRACT_DEBUG("Writing Diffusion Model Code Sequence"); DcmIODUtil::writeSingleItem(result, DCM_DiffusionModelCodeSequence, m_DiffusionModelCode, getData(), getRules()->getByTag(DCM_DiffusionModelCodeSequence)); + DCMTRACT_DEBUG("Writing Tracking Algorithm Identification Sequence"); DcmIODUtil::writeSubSequence(result, DCM_TrackingAlgorithmIdentificationSequence, m_TrackingAlgorithmIdentification, getData(), getRules()->getByTag(DCM_TrackingAlgorithmIdentificationSequence)); + DCMTRACT_DEBUG("Writing Track Statistics"); writeTrackStatistics(result, getData()); + DCMTRACT_DEBUG("Writing Track Set Statistics"); writeTrackSetStatistics(result, getData()); + DCMTRACT_DEBUG("Writing Track Set Measurements"); writeMeasurements(result, getData()); + DCMTRACT_DEBUG("Writing Track Set Tracks"); writeTracks(result, getData()); - if (result.good()) result = IODComponent::write(destination); + if (result.good()) + { + DCMTRACT_DEBUG("Writing Track Set details"); + result = IODComponent::write(destination); + } return result; } diff --git a/dcmtract/libsrc/trctractographyresults.cc b/dcmtract/libsrc/trctractographyresults.cc index e0624891..bea17882 100644 --- a/dcmtract/libsrc/trctractographyresults.cc +++ b/dcmtract/libsrc/trctractographyresults.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2016-2024, Open Connections GmbH + * Copyright (C) 2016-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -204,6 +204,10 @@ OFCondition TrcTractographyResults::write(DcmItem &dataset) // Common Instance Reference Module if (result.good()) result = DcmIODCommon::write(dataset); + // Write Tractography Results Series attributes which is not covered by a specific class + // but part of "this" class + if (result.good()) result = IODComponent::write(*getData(), *getRules(), dataset, "TractographyResultsSeries", getValueCheckOnWrite()); + return result; } diff --git a/dcmtract/libsrc/trctypes.cc b/dcmtract/libsrc/trctypes.cc index d2fbe94d..d20fe578 100644 --- a/dcmtract/libsrc/trctypes.cc +++ b/dcmtract/libsrc/trctypes.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2016, Open Connections GmbH + * Copyright (C) 2016-2025, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by @@ -25,10 +25,11 @@ OFLogger DCM_dcmtractLogger = OFLog::getLogger("dcmtk.dcmtract"); -makeOFConditionConst(TRC_EC_InvalidPointCoordinatesData, OFM_dcmtract, 1, OF_error, "Invalid data in Track's Point Coordinates Data element"); -makeOFConditionConst(TRC_EC_InvalidColorInformation, OFM_dcmtract, 2, OF_error, "Invalid color information for Track or Track Set"); -makeOFConditionConst(TRC_EC_NoSuchTrack, OFM_dcmtract, 3, OF_error, "No such Track in Track Set"); -makeOFConditionConst(TRC_EC_NoSuchMeasurement, OFM_dcmtract, 4, OF_error, "No such Measurement in Track Set"); -makeOFConditionConst(TRC_EC_MeasurementDataMissing, OFM_dcmtract, 5, OF_error, "Measurement misses data for one or more tracks"); -makeOFConditionConst(TRC_EC_InvalidStatisticData, OFM_dcmtract, 6, OF_error, "Statistic data is invalid"); -makeOFConditionConst(TRC_EC_InvalidTrackData, OFM_dcmtract, 7, OF_error, "Track data is invalid"); +makeOFConditionConst(TRC_EC_InvalidPointCoordinatesData, OFM_dcmtract, 1, OF_error, "Invalid data in Track's Point Coordinates Data element"); +makeOFConditionConst(TRC_EC_InvalidColorInformation, OFM_dcmtract, 2, OF_error, "Invalid color information for Track or Track Set"); +makeOFConditionConst(TRC_EC_NoSuchTrack, OFM_dcmtract, 3, OF_error, "No such Track in Track Set"); +makeOFConditionConst(TRC_EC_NoSuchMeasurement, OFM_dcmtract, 4, OF_error, "No such Measurement in Track Set"); +makeOFConditionConst(TRC_EC_MeasurementDataMissing, OFM_dcmtract, 5, OF_error, "Measurement misses data for one or more tracks"); +makeOFConditionConst(TRC_EC_InvalidStatisticData, OFM_dcmtract, 6, OF_error, "Statistic data is invalid"); +makeOFConditionConst(TRC_EC_InvalidTrackData, OFM_dcmtract, 7, OF_error, "Track data is invalid"); +makeOFConditionConst(TRC_EC_InvalidContentIdentification, OFM_dcmtract, 8, OF_error, "Invalid Content Identification for Tractography Results object"); diff --git a/dcmtract/tests/CMakeLists.txt b/dcmtract/tests/CMakeLists.txt new file mode 100644 index 00000000..badf82a8 --- /dev/null +++ b/dcmtract/tests/CMakeLists.txt @@ -0,0 +1,11 @@ +# declare executables +DCMTK_ADD_TEST_EXECUTABLE(dcmtract_tests + tcreate.cc + tests.cc +) + +# make sure executables are linked to the corresponding libraries +DCMTK_TARGET_LINK_MODULES(dcmtract_tests dcmtract) + +# This macro parses tests.cc and registers all tests +DCMTK_ADD_TESTS(dcmtract) diff --git a/dcmtract/tests/Makefile.dep b/dcmtract/tests/Makefile.dep index e69de29b..ff6a3ca6 100644 --- a/dcmtract/tests/Makefile.dep +++ b/dcmtract/tests/Makefile.dep @@ -0,0 +1,159 @@ +tcreate.o: tcreate.cc ../../config/include/dcmtk/config/osconfig.h \ + ../include/dcmtk/dcmtract/trctractographyresults.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcfilefo.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcelem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcobject.h \ + ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcerror.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctypes.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvr.h \ + ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \ + ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ + ../../dcmdata/include/dcmtk/dcmdata/dcstack.h \ + ../../dcmdata/include/dcmtk/dcmdata/dclist.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcitem.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodcommn.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodrules.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodtypes.h \ + ../../dcmiod/include/dcmtk/dcmiod/ioddef.h \ + ../../ofstd/include/dcmtk/ofstd/diag/vsconstexp.def \ + ../../ofstd/include/dcmtk/ofstd/ofmap.h \ + ../../dcmiod/include/dcmtk/dcmiod/modcommoninstanceref.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodmacro.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrlo.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcchrstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcbytstr.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvris.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrus.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrlt.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrcs.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcvrpn.h \ + ../../dcmiod/include/dcmtk/dcmiod/modbase.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodreferences.h \ + ../../dcmiod/include/dcmtk/dcmiod/modequipment.h \ + ../../dcmiod/include/dcmtk/dcmiod/modfor.h \ + ../../dcmiod/include/dcmtk/dcmiod/modgeneralseries.h \ + ../../dcmiod/include/dcmtk/dcmiod/modgeneralstudy.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpatient.h \ + ../../dcmiod/include/dcmtk/dcmiod/modpatientstudy.h \ + ../../dcmiod/include/dcmtk/dcmiod/modsopcommon.h \ + ../../dcmiod/include/dcmtk/dcmiod/modenhequipment.h \ + ../include/dcmtk/dcmtract/trctrackset.h \ + ../../dcmiod/include/dcmtk/dcmiod/iodutil.h \ + ../../ofstd/include/dcmtk/ofstd/ofdate.h \ + ../../ofstd/include/dcmtk/ofstd/oftime.h \ + ../include/dcmtk/dcmtract/trctypes.h ../include/dcmtk/dcmtract/trcdef.h \ + ../include/dcmtk/dcmtract/trcmodtractresults.h \ + ../../ofstd/include/dcmtk/ofstd/oftest.h \ + ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ + ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ + ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ + ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ + ../../ofstd/include/dcmtk/ofstd/ofexit.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcuid.h \ + ../../ofstd/include/dcmtk/ofstd/oftempf.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdict.h \ + ../../dcmdata/include/dcmtk/dcmdata/dchashdi.h +tests.o: tests.cc ../../config/include/dcmtk/config/osconfig.h \ + ../../ofstd/include/dcmtk/ofstd/oftest.h \ + ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ + ../../ofstd/include/dcmtk/ofstd/oftypes.h \ + ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofcast.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h \ + ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ + ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ + ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ + ../../ofstd/include/dcmtk/ofstd/oftraits.h \ + ../../ofstd/include/dcmtk/ofstd/oflist.h \ + ../../ofstd/include/dcmtk/ofstd/ofstring.h \ + ../../ofstd/include/dcmtk/ofstd/ofstream.h \ + ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ + ../../ofstd/include/dcmtk/ofstd/ofthread.h \ + ../../ofstd/include/dcmtk/ofstd/offile.h \ + ../../ofstd/include/dcmtk/ofstd/ofstd.h \ + ../../ofstd/include/dcmtk/ofstd/ofcond.h \ + ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ + ../../ofstd/include/dcmtk/ofstd/diag/push.def \ + ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ + ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ + ../../ofstd/include/dcmtk/ofstd/oflimits.h \ + ../../ofstd/include/dcmtk/ofstd/oferror.h \ + ../../ofstd/include/dcmtk/ofstd/ofexit.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcuid.h \ + ../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \ + ../../oflog/include/dcmtk/oflog/oflog.h \ + ../../oflog/include/dcmtk/oflog/logger.h \ + ../../oflog/include/dcmtk/oflog/config.h \ + ../../oflog/include/dcmtk/oflog/config/defines.h \ + ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ + ../../oflog/include/dcmtk/oflog/loglevel.h \ + ../../ofstd/include/dcmtk/ofstd/ofvector.h \ + ../../oflog/include/dcmtk/oflog/tstring.h \ + ../../oflog/include/dcmtk/oflog/tchar.h \ + ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ + ../../oflog/include/dcmtk/oflog/appender.h \ + ../../ofstd/include/dcmtk/ofstd/ofmem.h \ + ../../ofstd/include/dcmtk/ofstd/ofutil.h \ + ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ + ../../oflog/include/dcmtk/oflog/layout.h \ + ../../oflog/include/dcmtk/oflog/streams.h \ + ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ + ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ + ../../oflog/include/dcmtk/oflog/spi/filter.h \ + ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ + ../../oflog/include/dcmtk/oflog/spi/logfact.h \ + ../../oflog/include/dcmtk/oflog/logmacro.h \ + ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ + ../../oflog/include/dcmtk/oflog/tracelog.h diff --git a/dcmtract/tests/Makefile.in b/dcmtract/tests/Makefile.in index 644480a6..cbac4cdf 100644 --- a/dcmtract/tests/Makefile.in +++ b/dcmtract/tests/Makefile.in @@ -5,25 +5,58 @@ @SET_MAKE@ SHELL = /bin/sh +VPATH = @srcdir@:@top_srcdir@/include:@top_srcdir@/@configdir@/include srcdir = @srcdir@ top_srcdir = @top_srcdir@ configdir = @top_srcdir@/@configdir@ include $(configdir)/@common_makefile@ +oficonvdir = $(top_srcdir)/../oficonv +ofstddir = $(top_srcdir)/../ofstd +oflogdir = $(top_srcdir)/../oflog +dcmdatadir = $(top_srcdir)/../dcmdata +dcmioddir = $(top_srcdir)/../dcmiod +dcmfgdir = $(top_srcdir)/../dcmfg -all: +LOCALINCLUDES = -I$(top_srcdir)/include -I$(ofstddir)/include -I$(oflogdir)/include \ + -I$(dcmdatadir)/include -I$(dcmioddir)/include -I$(dcmfgdir)/include +LIBDIRS = -L$(top_srcdir)/libsrc -L$(oficonvdir)/libsrc -L$(ofstddir)/libsrc \ + -L$(oflogdir)/libsrc -L$(dcmdatadir)/libsrc -L$(dcmioddir)/libsrc \ + -L$(dcmfgdir)/libsrc +LOCALLIBS = -ldcmtract -ldcmfg -ldcmiod -ldcmdata -loflog -lofstd -loficonv \ + $(ZLIBLIBS) $(CHARCONVLIBS) $(MATHLIBS) -check: +test_objs = tcreate.o tests.o -check-exhaustive: +objs = $(test_objs) +progs = tests + + +all: $(progs) + +tests: $(test_objs) + $(CXX) $(CXXFLAGS) $(LIBDIRS) $(LDFLAGS) -o $@ $(test_objs) $(LOCALLIBS) $(LIBS) + + +check: tests + DCMDICTPATH=../../dcmdata/data/dicom.dic ./tests + +check-exhaustive: tests + DCMDICTPATH=../../dcmdata/data/dicom.dic ./tests -x + + +install: all -install: clean: - rm -f $(TRASH) + rm -f $(objs) $(progs) $(TRASH) distclean: - rm -f $(DISTTRASH) + rm -f $(objs) $(progs) $(DISTTRASH) + dependencies: + $(CXX) -MM $(defines) $(includes) $(CPPFLAGS) $(CXXFLAGS) *.cc > $(DEP) + +include $(DEP) diff --git a/dcmtract/tests/tcreate.cc b/dcmtract/tests/tcreate.cc new file mode 100644 index 00000000..6ec7a15c --- /dev/null +++ b/dcmtract/tests/tcreate.cc @@ -0,0 +1,132 @@ +/* + * + * Copyright (C) 2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmtract + * + * Author: Michael Onken + * + * Purpose: Tests that check for pixel data overflow conditions + * + */ + + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ + +#include "dcmtk/dcmtract/trctractographyresults.h" +#include "dcmtk/dcmdata/dcfilefo.h" +#include "dcmtk/ofstd/oftest.h" +#include "dcmtk/ofstd/oftempf.h" +#include "dcmtk/dcmdata/dcdict.h" + +static OFString EXPECTED_DUMP; + +static TrcTractographyResults *create(); +static void setGenericValues(TrcTractographyResults *ct); + + +OFTEST(dcmtract_create) +{ + /* make sure data dictionary is loaded */ + if (!dcmDataDict.isDictionaryLoaded()) + { + OFCHECK(dcmDataDict.isDictionaryLoaded()); + } + + // Creation + TrcTractographyResults* tract(create()); + OFCHECK(tract != OFnullptr); + + + setGenericValues(tract); + + // Write to dataset and compare its dump with expected result + DcmFileFormat dcmff; + + // We just use this to create a temporary file name + OFTempFile tf(O_RDWR, "", "dcmtract_create", ".dcm"); + OFCondition result; + result = tract->saveFile(tf.getFilename(), EXS_LittleEndianExplicit); + OFCHECK_MSG(result == EC_Normal, result.text()); + delete tract; // delete tractography results object +} + +static TrcTractographyResults *create() +{ + IODEnhGeneralEquipmentModule::EquipmentInfo eq("Open Connections", "OC CT", "4711", "0.1"); + TrcTractographyResults *tract = NULL; + OFCondition result; + result = TrcTractographyResults::create(ContentIdentificationMacro("1", "LABEL", "Description", "Open Connections"), + "20250527", + "103100", + eq, + IODReferences(), + tract); + + OFCHECK(result.good()); + OFCHECK(tract != OFnullptr); + return tract; +} + + +static void setGenericValues(TrcTractographyResults *tract) +{ + if (!tract) + return; + OFCHECK(tract->getPatient().setPatientName("Bond^James").good()); + OFCHECK(tract->getPatient().setPatientID("007").good()); + OFCHECK(tract->getPatient().setPatientBirthDate("19771007").good()); + OFCHECK(tract->getStudy().setStudyDate("20250101").good()); + OFCHECK(tract->getStudy().setStudyTime("120000").good()); + OFCHECK(tract->getStudy().setStudyID("1").good()); + OFCHECK(tract->getPatientStudy().setPatientAge("047Y").good()); + OFCHECK(tract->getSeries().setSeriesDescription("Test Description").good()); + OFCHECK(tract->getSeries().setSeriesNumber("1").good()); + + // Those values are usually computed automatically. UIDS are generated and date/times are set to current values. + // But in order to compare the "old" dump with the freshly created image attributes, we set some values manually, + // so that they are not overwritten with new, automatically created values later. + OFCHECK(tract->getStudy().setStudyInstanceUID("1.2.276.0.7230010.3.1.2.8323329.14863.1565940357.864811").good()); + OFCHECK(tract->getFrameOfReference().setFrameOfReferenceUID("2.25.30853397773651184949181049330553108086").good()); + OFCHECK(tract->getSeries().setSeriesInstanceUID("1.2.276.0.7230010.3.1.3.8323329.14863.1565940357.864812").good()); + OFCHECK(tract->getSOPCommon().setSOPInstanceUID("1.2.276.0.7230010.3.1.4.8323329.14863.1565940357.864813").good()); + + // Create trackset + CodeWithModifiers trackSetAnatomy("3", "1"); + trackSetAnatomy.set("12738006", "SCT", "Brain"); + // Create algo identification macro + AlgorithmIdentificationMacro algo; + algo.getAlgorithmFamilyCode().set("4711", "99OC", "Dummy Tracking"); + algo.setAlgorithmName("Deterministic Tracking"); + algo.setAlgorithmVersion("1.0"); + algo.setAlgorithmParameters("Parameter1=Value1\\Parameter2=Value2"); + algo.setAlgorithmSource("Open Connections"); + + TrcTrackSet* tractSet = NULL; + + // Add trackset to tractography results + OFCondition result = tract->addTrackSet("TRACK_SET_LABEL", "Track Set Description", + trackSetAnatomy, + CodeSequenceMacro("113231", "DCM", "Single Tensor"), + algo, tractSet); + + OFCHECK(result.good()); + OFCHECK(tractSet != OFnullptr); + + // Add tracks to trackset + Float32 points[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f }; + Uint16 colors[] = { 0, 0, 0 }; + TrcTrack* track = NULL; + result = tractSet->addTrack(points, 2, colors, 1, track); + OFCHECK(result.good()); + OFCHECK(track != OFnullptr); +} diff --git a/dcmtract/tests/tests.cc b/dcmtract/tests/tests.cc new file mode 100644 index 00000000..68fc25c8 --- /dev/null +++ b/dcmtract/tests/tests.cc @@ -0,0 +1,28 @@ +/* + * + * Copyright (C) 2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmtract + * + * Author: Michael Onken + * + * Purpose: main test program + * + */ + +#include "dcmtk/config/osconfig.h" + +#include "dcmtk/ofstd/oftest.h" + +OFTEST_REGISTER(dcmtract_create); + +OFTEST_MAIN("dcmtract") diff --git a/dcmwlm/apps/CMakeLists.txt b/dcmwlm/apps/CMakeLists.txt index 45f0fdd3..9131b13a 100644 --- a/dcmwlm/apps/CMakeLists.txt +++ b/dcmwlm/apps/CMakeLists.txt @@ -5,4 +5,4 @@ include_directories("${dcmtls_SOURCE_DIR}/include") DCMTK_ADD_EXECUTABLE(wlmscpfs wlmscpfs.cc wlcefs.cc) # make sure executables are linked to the corresponding libraries -DCMTK_TARGET_LINK_MODULES(wlmscpfs dcmwlm dcmnet dcmtls dcmdata oflog ofstd) +DCMTK_TARGET_LINK_MODULES(wlmscpfs dcmwlm) diff --git a/dcmwlm/apps/wlcefs.cc b/dcmwlm/apps/wlcefs.cc index 345eb644..685a4412 100644 --- a/dcmwlm/apps/wlcefs.cc +++ b/dcmwlm/apps/wlcefs.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2024, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -136,6 +136,11 @@ WlmConsoleEngineFileSystem::WlmConsoleEngineFileSystem( int argc, char *argv[], cmd->addOption("--no-sq-expansion", "-nse", "disable expansion of empty sequences in C-FIND\nrequest messages"); cmd->addGroup("network options:"); + cmd->addSubGroup("IP protocol version:"); + cmd->addOption("--ipv4", "-i4", "use IPv4 only (default)"); + cmd->addOption("--ipv6", "-i6", "use IPv6 only"); + cmd->addOption("--ip-auto", "-i0", "use IPv6/IPv4 dual stack"); + cmd->addSubGroup("preferred network transfer syntaxes:"); cmd->addOption("--prefer-uncompr", "+x=", "prefer explicit VR local byte order (default)"); cmd->addOption("--prefer-little", "+xe", "prefer explicit VR little endian TS"); @@ -255,6 +260,14 @@ WlmConsoleEngineFileSystem::WlmConsoleEngineFileSystem( int argc, char *argv[], if( cmd->findOption("--no-sq-expansion") ) opt_noSequenceExpansion = OFTrue; // network options + + // set the IP protocol version + cmd->beginOptionBlock(); + if (cmd->findOption("--ipv4")) dcmIncomingProtocolFamily.set(ASC_AF_INET); + if (cmd->findOption("--ipv6")) dcmIncomingProtocolFamily.set(ASC_AF_INET6); + if (cmd->findOption("--ip-auto")) dcmIncomingProtocolFamily.set(ASC_AF_UNSPEC); + cmd->endOptionBlock(); + cmd->beginOptionBlock(); if( cmd->findOption("--prefer-uncompr") ) opt_networkTransferSyntax = EXS_Unknown; if( cmd->findOption("--prefer-little") ) opt_networkTransferSyntax = EXS_LittleEndianExplicit; diff --git a/dcmwlm/docs/wlmscpfs.man b/dcmwlm/docs/wlmscpfs.man index a0c096b3..02d87b07 100644 --- a/dcmwlm/docs/wlmscpfs.man +++ b/dcmwlm/docs/wlmscpfs.man @@ -108,6 +108,17 @@ other processing options: \subsection wlmscpfs_network_options network options \verbatim +IP protocol version: + + -i4 --ipv4 + use IPv4 only (default) + + -i6 --ipv6 + use IPv6 only + + -i0 --ip-auto + use IPv6/IPv4 dual stack + preferred network transfer syntaxes: +x= --prefer-uncompr @@ -380,7 +391,7 @@ As return keys the following attributes are currently supported by \b wlmscpfs: (0010,1080) MilitaryRank (0010,2000) MedicalAlerts (0010,2110) ContrastAllergies -(0010,2160) EthnicGroup +(0010,2160) EthnicGroup (retired) (0010,21a0) SmokingStatus (0010,21b0) AdditionalPatientHistory (0010,21c0) PregnancyStatus @@ -515,6 +526,6 @@ It is an error if no data dictionary can be loaded. \section wlmscpfs_copyright COPYRIGHT -Copyright (C) 1996-2024 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. +Copyright (C) 1996-2025 by OFFIS e.V., Escherweg 2, 26121 Oldenburg, Germany. */ diff --git a/dcmwlm/include/dcmtk/dcmwlm/wlds.h b/dcmwlm/include/dcmtk/dcmwlm/wlds.h index 4a076bcb..7284d92d 100644 --- a/dcmwlm/include/dcmtk/dcmwlm/wlds.h +++ b/dcmwlm/include/dcmtk/dcmwlm/wlds.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2023, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -211,7 +211,7 @@ class DCMTK_DCMWLM_EXPORT WlmDataSource * DCM_AdmittingDiagnosesDescription (0008,1080) LO O 3 (from the Visit Admission Module) * DCM_RETIRED_OtherPatientIDs (0010,1000) LO O 3 (from the Patient Identification Module) * DCM_PatientSize (0010,1020) DS O 3 (from the Patient Demographic Module) - * DCM_EthnicGroup (0010,2160) SH O 3 (from the Patient Demographic Module) + * DCM_RETIRED_EthnicGroup (0010,2160) SH O 3 (from the Patient Demographic Module) * DCM_PatientComments (0010,4000) LT O 3 (from the Patient Demographic Module) * DCM_AdditionalPatientHistory (0010,21b0) LT O 3 (from the Patient Medical Module) * DCM_LastMenstrualDate (0010,21d0) DA O 3 (from the Patient Medical Module) diff --git a/dcmwlm/libsrc/wlds.cc b/dcmwlm/libsrc/wlds.cc index 635af2fb..0c4ac472 100644 --- a/dcmwlm/libsrc/wlds.cc +++ b/dcmwlm/libsrc/wlds.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2023, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -1071,7 +1071,7 @@ OFBool WlmDataSource::IsSupportedReturnKeyAttribute( DcmElement *element, DcmSeq // DCM_AdmittingDiagnosesDescription (0008,1080) LO O 3 (from the Visit Admission Module) // DCM_RETIRED_OtherPatientIDs (0010,1000) LO O 3 (from the Patient Identification Module) // DCM_PatientSize (0010,1020) DS O 3 (from the Patient Demographic Module) -// DCM_EthnicGroup (0010,2160) SH O 3 (from the Patient Demographic Module) +// DCM_RETIRED_EthnicGroup (0010,2160) SH O 3 (from the Patient Demographic Module) // DCM_PatientComments (0010,4000) LT O 3 (from the Patient Demographic Module) // DCM_AdditionalPatientHistory (0010,21b0) LT O 3 (from the Patient Medical Module) // DCM_LastMenstrualDate (0010,21d0) DA O 3 (from the Patient Medical Module) @@ -1195,7 +1195,7 @@ OFBool WlmDataSource::IsSupportedReturnKeyAttribute( DcmElement *element, DcmSeq elementKey == DCM_AdmittingDiagnosesDescription || elementKey == DCM_RETIRED_OtherPatientIDs || elementKey == DCM_PatientSize || - elementKey == DCM_EthnicGroup || + elementKey == DCM_RETIRED_EthnicGroup || elementKey == DCM_PatientComments || elementKey == DCM_AdditionalPatientHistory || elementKey == DCM_LastMenstrualDate || diff --git a/dcmwlm/libsrc/wldsfs.cc b/dcmwlm/libsrc/wldsfs.cc index d821bb79..ee83d7b0 100644 --- a/dcmwlm/libsrc/wldsfs.cc +++ b/dcmwlm/libsrc/wldsfs.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1996-2023, OFFIS e.V. + * Copyright (C) 1996-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -23,9 +23,7 @@ #include "dcmtk/config/osconfig.h" // specific configuration for operating system BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include // for O_RDWR -#endif END_EXTERN_C #include "dcmtk/ofstd/oftypes.h" #include "dcmtk/ofstd/ofstd.h" diff --git a/dcmwlm/tests/CMakeLists.txt b/dcmwlm/tests/CMakeLists.txt index a92cf91d..458840d6 100644 --- a/dcmwlm/tests/CMakeLists.txt +++ b/dcmwlm/tests/CMakeLists.txt @@ -2,4 +2,4 @@ DCMTK_ADD_TEST_EXECUTABLE(wltest wltest.cc) # make sure executables are linked to the corresponding libraries -DCMTK_TARGET_LINK_MODULES(wltest dcmwlm dcmnet dcmtls dcmdata oflog ofstd) +DCMTK_TARGET_LINK_MODULES(wltest dcmwlm dcmtls) diff --git a/docs/ANNOUNCE.369 b/docs/ANNOUNCE.369 new file mode 100644 index 00000000..646567c9 --- /dev/null +++ b/docs/ANNOUNCE.369 @@ -0,0 +1,168 @@ +ANNOUNCEMENT + +Version 3.6.9 of the OFFIS DCMTK (DICOM toolkit) software is now available for +public release. This release includes the following main changes over the +previous version 3.6.8: + +- DCMTK 3.6.9 builds correctly on older and up-to-date versions of GNU gcc + (9.5.0 to 14.2.0), Clang (14.0.6 to 18.1.8), Apple Clang (14.0.3 to 15.0.0), + and Microsoft Visual Studio (2017 to 2022). + +- Tested with the following operating systems/environments: + - Android on arm64 + - FreeBSD on x86_64 + - Linux on x86_64 and x86 + - MacOS X on x86_64 and arm64 + - NetBSD on x86_64 + - OpenBSD on x86_64 + - OpenIndiana on x86_64 + - Windows (including MinGW) on x86_64 and x86 + + For a complete list of tested systems and compilers, see the INSTALL file. + +- Updated DICOM data dictionary, list of SOP classes, well-known frame of + references, transfer syntaxes, code definitions, supported context group + classes, and directory record types for DICOM standard release 2024e: + + - This also includes the latest attributes and SOP classes for the DICONDE + standard, e.g. for thermography images (based on ASTM E3440). + + - Also updated the DICOMDIR generation code and tools accordingly. + + +- The new JPEG XL and HTJ2K transfer syntaxes as well as the encapsulated + uncompressed transfer syntax are now supported for reading and writing, i.e. + for both files and network transfer. However, encoders or decoders have not + been implemented yet. + +- Added new command line tool dcm2img that unifies and replaces the tools + dcm2pnm, dcmj2pnm and dcml2pnm, and adds support for JPEG-LS as an export + format for image files. The command line options are identical to the older + tools, so that dcm2img can serve as a drop-in replacement: + + - By default, the new command line tool determines the output format + automatically based on the extension of the output filename. + + - The deprecated command line tools were replaced by stubs, which are provided + for the user's convenience, but will be removed with a future release. + +- Added new command line tool dcm2cda that extracts a CDA document from a DICOM + Encapsulated CDA Storage SOP Instance and stores it in a separate file. + +- Replaced command line tool dcmgpdir by a stub that calls the more + comprehensive command line tool dcmmkdir. + +- Further enhanced and updated DICOM Structured Reporting (SR) module "dcmsr": + + - Added support for the new Waveform Annotation SR IOD (introduced with + Supplement 239). + + - Made URL prefix for hyperlinks to composite objects configurable. + + - Updated code definitions and supported context group classes (see above). + + - Fixed issue with various IOD constraint checkers (see CP-2084). + +- Added IPv6 support to DCMTK's association requestors. All DCMTK "client" + applications that only request outgoing DICOM network associations can now + explicitly select the protocol version to be used. IPv6 support is not yet + implemented for association acceptors ("server" applications). + +- Various TLS enhancements: + + - Added TLS support to the command line tools dcmqrscp and getscu. + + - Added support for the Modified BCP 195 RFC 8996 TLS Profile. + + - Added new command line option --list-profiles to all TLS-enabled tools. + This option prints a list of the TLS Secure Transport Connection Profiles + supported. + + - Removed support for OpenSSL 1.0.2 and 1.1.0 and added support for OpenSSL + 3.1.0 to 3.4.0. + +- Extended central DCMTK data structure where all SOP Classes are defined with + their associated properties, e.g. type and sub-type. + +- Largely enhanced basic transfer syntax class DcmXfer, e.g. to distinguish + more clearly between encapsulation and compression. Please note that some of + the old methods have been deprecated and will be removed in a future release. + +- Enhanced performance of OFGlobal class, especially when used in applications + with many threads that read global objects of this class concurrently. + +- New, fully standards compliant implementations of OFStandard::atof() and + OFStandard::ftoa(), DCMTK's locale independent conversion routines between + floating point numbers and text. + +- Removed support for ICU-based character set conversion. Since the oficonv + module in DCMTK supports all DICOM Specific Character Sets, the ICU support, + which was never complete, has been removed. + +- DCMTK now requires compilers to provide conformance to C++98 and supports + compilation with newer C++ versions up to C++20, which can be enabled via + CMake's CMAKE_CXX_STANDARD variable. By default, C++11 is now enabled on + compilers that support this. + +- CMake-related enhancements and other changes: + + - The configure process now respects CMake's CMAKE_CROSSCOMPILING_EMULATOR + variable. + + - Exposed the CMAKE_DEBUG_POSTFIX variable to the user. There are extra + options to also enable the postfix for Windows DLLs as well as executables. + +- Many configure tests related to outdated compilers or libraries were removed, + thus significantly speeding up the configuration process. + +- Fixed binary segmentations with certain dimensions (some cases where number + of total bits per frame is not divisible by 8) that were broken when being + serialized into a dataset. + +- Fixed various other issues that occurred after the official 3.6.8 release, + and further improved the performance. See CHANGES file for details. + +Many people have contributed to this new release of DCMTK, appearing here in +alphabetical order. Thank you very much for your support! + + Christian Wetzel + David Gobbi + David Seifert + Giulio Simonetti + Helmut Steiner + Jean Pierre Bassenge + Jean-Christophe Fillion-Robin + Jesper Alf Dam + Kevin Leonardic + Marcel Pham + Mario Galijot + Markus Sabin + Martin Zeiser of the Cisco Talos team + Mathieu Malaterre + Matt McCormick + Melanie Michels + Nils Bars + Peter Klotz + Phileas Lebada + Piotr Batko + Sam James + Sobhita Mercy + Yoshinaga Kosuke + + DCMTK forum users "andreasb", "Fabian Guenther", "nbeck", "Oleh", "saltcreek" + + GitHub users "akaraivanov", "bananabr", "khangthk", "luissantosHCIT", + "malaterre", "mrbean-bremen", "percontation", "thewtex" + +Members of the DCMTK Team who have worked on this release are: + + Joerg Riesmeier + Marco Eichelberg + Michael Onken + Tingyan Xu + +The DCMTK software can be downloaded via: + + https://dicom.offis.de/dcmtk or https://www.dcmtk.org/ + +OFFIS e.V., Oldenburg, Germany, 2024-12-10 diff --git a/docs/CHANGES.370 b/docs/CHANGES.370 new file mode 100644 index 00000000..22c33f9f --- /dev/null +++ b/docs/CHANGES.370 @@ -0,0 +1,3027 @@ + +Release 3.7.0 (Public Minor Release - 2025-12-15) + +**** Changes from 2025.12.15 (eichelberg) + +- Created CHANGES.370 for DCMTK release 3.7.0: + CHANGES.370 contains the Git commit history since DCMTK release 3.6.9. + Added: docs/CHANGES.370 + +- Updated man pages for DCMTK release 3.7.0. + Added: doxygen/manpages/man1/dcmdecap.1 + doxygen/manpages/man1/dcmencap.1 + doxygen/manpages/man1/json2dcm.1 + Affects: doxygen/manpages/man1/cda2dcm.1 + doxygen/manpages/man1/dcm2cda.1 + doxygen/manpages/man1/dcm2img.1 + doxygen/manpages/man1/dcm2json.1 + doxygen/manpages/man1/dcm2pdf.1 + doxygen/manpages/man1/dcm2pnm.1 + doxygen/manpages/man1/dcm2xml.1 + doxygen/manpages/man1/dcmcjpeg.1 + doxygen/manpages/man1/dcmcjpls.1 + doxygen/manpages/man1/dcmconv.1 + doxygen/manpages/man1/dcmcrle.1 + doxygen/manpages/man1/dcmdjpeg.1 + doxygen/manpages/man1/dcmdjpls.1 + doxygen/manpages/man1/dcmdrle.1 + doxygen/manpages/man1/dcmdspfn.1 + doxygen/manpages/man1/dcmdump.1 + doxygen/manpages/man1/dcmftest.1 + doxygen/manpages/man1/dcmgpdir.1 + doxygen/manpages/man1/dcmicmp.1 + doxygen/manpages/man1/dcmj2pnm.1 + doxygen/manpages/man1/dcml2pnm.1 + doxygen/manpages/man1/dcmmkcrv.1 + doxygen/manpages/man1/dcmmkdir.1 + doxygen/manpages/man1/dcmmklut.1 + doxygen/manpages/man1/dcmodify.1 + doxygen/manpages/man1/dcmp2pgm.1 + doxygen/manpages/man1/dcmprscp.1 + doxygen/manpages/man1/dcmprscu.1 + doxygen/manpages/man1/dcmpschk.1 + doxygen/manpages/man1/dcmpsmk.1 + doxygen/manpages/man1/dcmpsprt.1 + doxygen/manpages/man1/dcmpsrcv.1 + doxygen/manpages/man1/dcmpssnd.1 + doxygen/manpages/man1/dcmqridx.1 + doxygen/manpages/man1/dcmqrscp.1 + doxygen/manpages/man1/dcmqrti.1 + doxygen/manpages/man1/dcmquant.1 + doxygen/manpages/man1/dcmrecv.1 + doxygen/manpages/man1/dcmscale.1 + doxygen/manpages/man1/dcmsend.1 + doxygen/manpages/man1/dcmsign.1 + doxygen/manpages/man1/dcod2lum.1 + doxygen/manpages/man1/dconvlum.1 + doxygen/manpages/man1/drtdump.1 + doxygen/manpages/man1/dsr2html.1 + doxygen/manpages/man1/dsr2xml.1 + doxygen/manpages/man1/dsrdump.1 + doxygen/manpages/man1/dump2dcm.1 + doxygen/manpages/man1/echoscu.1 + doxygen/manpages/man1/findscu.1 + doxygen/manpages/man1/getscu.1 + doxygen/manpages/man1/img2dcm.1 + doxygen/manpages/man1/mkcsmapper.1 + doxygen/manpages/man1/mkesdb.1 + doxygen/manpages/man1/movescu.1 + doxygen/manpages/man1/pdf2dcm.1 + doxygen/manpages/man1/stl2dcm.1 + doxygen/manpages/man1/storescp.1 + doxygen/manpages/man1/storescu.1 + doxygen/manpages/man1/termscu.1 + doxygen/manpages/man1/wlmscpfs.1 + doxygen/manpages/man1/xml2dcm.1 + doxygen/manpages/man1/xml2dsr.1 + +- Updated version information for DCMTK release 3.7.0. + Affects: CMake/dcmtkPrepare.cmake + +- Updated copyright date. + Affects: dcmdata/docs/dcm2xml.man + dcmdata/docs/dcmconv.man + dcmdata/docs/dcmcrle.man + dcmdata/docs/dcmdrle.man + dcmdata/docs/dcmdump.man + dcmdata/docs/dcmftest.man + dcmdata/docs/dcmgpdir.man + dcmdata/docs/dcmodify.man + dcmdata/docs/dump2dcm.man + dcmdata/docs/img2dcm.man + dcmdata/docs/xml2dcm.man + dcmimage/docs/dcm2pnm.man + dcmimage/docs/dcmicmp.man + dcmimage/docs/dcmquant.man + dcmimage/docs/dcmscale.man + dcmimgle/docs/dcmdspfn.man + dcmimgle/docs/dcod2lum.man + dcmimgle/docs/dconvlum.man + dcmjpeg/docs/dcmcjpeg.man + dcmjpeg/docs/dcmj2pnm.man + dcmjpeg/docs/dcmmkdir.man + dcmjpls/docs/dcmcjpls.man + dcmjpls/docs/dcmdjpls.man + dcmjpls/docs/dcml2pnm.man + dcmnet/docs/dcmsend.man + dcmnet/docs/findscu.man + dcmnet/docs/storescu.man + dcmnet/docs/termscu.man + dcmpstat/docs/dcmmkcrv.man + dcmpstat/docs/dcmmklut.man + dcmpstat/docs/dcmp2pgm.man + dcmpstat/docs/dcmpschk.man + dcmpstat/docs/dcmpsmk.man + dcmpstat/docs/dcmpsprt.man + dcmpstat/docs/dcmpsrcv.man + dcmpstat/docs/dcmpssnd.man + dcmqrdb/docs/dcmqridx.man + dcmqrdb/docs/dcmqrti.man + dcmrt/docs/drtdump.man + dcmsign/docs/dcmsign.man + +- Updated autoconf files for upcoming release. + Affects: VERSION + config/configure + config/configure.in + config/confmod + +- Updated URL for Windows support libs. + Affects: .github/workflows/cmake-win.yml + +- Updated documentation for upcoming release. + Affects: ANNOUNCE + CREDITS + INSTALL + +**** Changes from 2025.12.12 (eichelberg) + +- Fixed warnings reported on Android. + Affects: dcmpstat/apps/dcmpssnd.cc + dcmqrdb/libsrc/dcmqrcbg.cc + dcmqrdb/libsrc/dcmqrcbm.cc + dcmqrdb/libsrc/dcmqrdbi.cc + dcmqrdb/libsrc/dcmqrsrv.cc + dcmqrdb/libsrc/dcmqrtis.cc + ofstd/include/dcmtk/ofstd/ofstd.h + ofstd/libsrc/offilsys.cc + +- Fixed documentation: + Fixed an incorrect API documentation. + Thanks to Matt Hancock for the report. + Affects: dcmnet/include/dcmtk/dcmnet/scp.h + dcmnet/include/dcmtk/dcmnet/scpcfg.h + +**** Changes from 2025.12.11 (goldhammer) + +- Updated CMake files for Android API 35: + Updated the CMake toolchain for Android API level 35 and Android NDK R27. + Affects: CMake/CTest/CTestCustomAndroid.cmake.in + CMake/CTest/dcmtkCTestRunAndroid.cmake.in + CMake/dcmtkMacros.cmake + CMake/dcmtkUseAndroidSDK.cmake + CMakeLists.txt + +**** Changes from 2025.12.09 (riesmeier) + +- Avoid wrong warning message on private pixel data: + Made sure that a warning message on compressed private pixel data is + only reported if the element length is really undefined. + Thanks to Mathieu Malaterre for the + original report. + Affects: dcmdata/libsrc/dcitem.cc + +**** Changes from 2025.12.08 (riesmeier) + +- Fixed issue when charset conversion is disabled: + Fixed compilation error when character set conversion has been disabled + during configuration process. + Affects: dcmsr/apps/dsrdump.cc + +**** Changes from 2025.12.02 (eichelberg) + +- Fixed bug in handling of odd-length data elements: + When a dataset containing an illegal odd-length attribute with a text VR + was read from file or received over a network connection, then accessing + the value of that attribute with DcmElement::getString() may return a + pointer to a string that was not properly null terminated. Using C string + functions such as strlen() or strcpy() on that string then lead to a read + beyond the end of a string, causing a segmentation fault. + Thanks to Zou Dikai for the bug report and POC. + This closes DCMTK issue #1184. + Affects: dcmdata/libsrc/dcbytstr.cc + +**** Changes from 2025.11.28 (riesmeier) + +- Updated information on latest CMake version: + Changed latest CMake version that was tested to 4.2.0. + Affects: CMakeLists.txt + +**** Changes from 2025.11.28 (eichelberg) + +- Fixed two possible segfaults in dcmqrscp: + Fixed two places where invalid messages may trigger a segmentation fault + due to a NULL pointer being de-referenced. + Thanks to 邹 迪凯 for the bug report and proof-of-concept. + Affects: dcmqrdb/libsrc/dcmqrdbi.cc + +**** Changes from 2025.11.27 (eichelberg) + +- Fixed errors when compiling with C++98. + Affects: dcmseg/include/dcmtk/dcmseg/overlaputil.h + dcmseg/libsrc/segdoc.cc + +**** Changes from 2025.11.26 (onken) + +- Fix compiler warning on unreachable code. + Affects: dcmseg/libsrc/overlaputil.cc + +**** Changes from 2025.11.26 (eichelberg) + +- Fixed strlcpy related issue in oficonv: + Replaced two calls of strlcpy with strncpy in places where the source + string is not guaranteed to be always null terminated, which strlcpy requires. + Thanks to DCMTK forum user "saltcreek" for the original report. + Affects: oficonv/libsrc/citrus_mapper.c + +**** Changes from 2025.11.25 (onken) + +- Avoid compiler warning for VS 2022 on Windows 10. + Affects: dcmseg/libsrc/overlaputil.cc + +**** Changes from 2025.11.25 (riesmeier) + +- Fixed wrong use of ESC in "LO" and "SH" test case: + Thanks to GitHub user "mrbean-bremen" for the original report and + proposed patch (see PR #124). + Affects: dcmdata/tests/tchval.cc + +- Updated copyright date. + Affects: COPYRIGHT + +**** Changes from 2025.11.25 (onken) + +- Fix compiler warnings/errors from 74bba5. + Affects: dcmfg/include/dcmtk/dcmfg/framesorter.h + dcmseg/include/dcmtk/dcmseg/overlaputil.h + dcmseg/libsrc/overlaputil.cc + dcmseg/libsrc/segment.cc + +**** Changes from 2025.11.24 (onken) + +- Re-add dcmsign to the Makefile: + Accidentally removed in the last commit. + Affects: Makefile + +- OverlapUtil and some segmentation enhancements: + Added OverlapUtil class which allows to detect whether a binary + segmentation contains overlapping segments. This supports an upcoming + converter for converting binary segmentation objects into labelmap + segmentations. + Various small enhancements. + Added: dcmfg/include/dcmtk/dcmfg/framesorter.h + dcmiod/include/dcmtk/dcmiod/iccexample.h + dcmiod/tests/tmacro.cc + dcmseg/include/dcmtk/dcmseg/overlaputil.h + dcmseg/libsrc/overlaputil.cc + Affects: Makefile + config/math.cc + dcmdata/apps/Makefile.dep + dcmdata/libsrc/Makefile.dep + dcmect/libsrc/Makefile.dep + dcmect/tests/Makefile.dep + dcmfg/libsrc/Makefile.dep + dcmfg/libsrc/fginterface.cc + dcmfg/tests/Makefile.dep + dcmiod/include/dcmtk/dcmiod/iodrules.h + dcmiod/include/dcmtk/dcmiod/iodtypes.h + dcmiod/include/dcmtk/dcmiod/modequipment.h + dcmiod/include/dcmtk/dcmiod/modiccprofile.h + dcmiod/libsrc/Makefile.dep + dcmiod/libsrc/iodmacro.cc + dcmiod/libsrc/iodrules.cc + dcmiod/libsrc/modequipment.cc + dcmiod/libsrc/modiccprofile.cc + dcmiod/tests/CMakeLists.txt + dcmiod/tests/Makefile.dep + dcmiod/tests/Makefile.in + dcmiod/tests/tests.cc + dcmpmap/libsrc/Makefile.dep + dcmseg/Makefile.in + dcmseg/include/dcmtk/dcmseg/segdoc.h + dcmseg/include/dcmtk/dcmseg/segment.h + dcmseg/include/dcmtk/dcmseg/segtypes.h + dcmseg/include/dcmtk/dcmseg/segutils.h + dcmseg/libsrc/CMakeLists.txt + dcmseg/libsrc/Makefile.dep + dcmseg/libsrc/Makefile.in + dcmseg/libsrc/segdoc.cc + dcmseg/libsrc/segment.cc + dcmseg/libsrc/segtypes.cc + dcmseg/libsrc/segutils.cc + dcmseg/tests/Makefile.dep + dcmseg/tests/Makefile.in + dcmsr/apps/Makefile.dep + dcmsr/libsrc/Makefile.dep + dcmsr/tests/Makefile.dep + dcmtract/libsrc/Makefile.dep + dcmtract/tests/Makefile.dep + ofstd/include/dcmtk/ofstd/ofmath.h + ofstd/libsrc/ofmath.cc + +**** Changes from 2025.11.23 (onken) + +- Updated copyright date. + Affects: dcmnet/include/dcmtk/dcmnet/scppool.h + +- Fix potential crashes and endless loop: + Fix crash when worker thread reports to the owner pool after destructor + of the pool was called. + There is no guarantee that all worker threads have completed their work + when when m_runMode is set to SHUTDOWN. Added an additional condition to + the while loop to wait until all workers have completed their tasks and + reported to the pool. After that it's guarantied that all workers are in + the m_workerIdle list and can be safely joined. + --- + Fixed two cases that make destructor of DcmBaseSCPPool never exit. + Current implementation of DcmBaseSCPPool's destructor waits for the + m_runMode to be set to SHUTDOWN. If m_runMode never set to SHUTDOWN the + destructor will wait forever in the 'while' cycle. Two cases when + m_runMode is not set to SHUTDOWN: + Case 1: The m_runMode won't be set to SHUTDOWN if + DcmBaseSCPPool::listen() was never called. + Fix: + Changed default value of m_runMode from LISTEN to SHUTDOWN. The + m_runMode is set to LISTEN inside of the DcmBaseSCPPool::listen() + function. + Case 2: + Call to initializeNetwork() in the DcmBaseSCPPool::listen() function + might return bad condition. In that case m_runMode won't be set to + SHUTDOWN due to early return. + Fix: + Add a call to the newly added finishListening() function in case of + early return. + Thanks to GitHub user Vovasch for the report and patch. + Affects: dcmnet/include/dcmtk/dcmnet/scppool.h + dcmnet/libsrc/scppool.cc + +**** Changes from 2025.11.21 (riesmeier) + +- Updated Context Group classes for DICOM 2025e: + Updated automatically generated Context Group classes for the latest + release of the DICOM standard. All supported classes were updated, + even though there were no changes to most of them. + Affects: dcmsr/include/dcmtk/dcmsr/cmr/cid100.h + dcmsr/include/dcmtk/dcmsr/cmr/cid10013.h + dcmsr/include/dcmtk/dcmsr/cmr/cid10033.h + dcmsr/include/dcmtk/dcmsr/cmr/cid11.h + dcmsr/include/dcmtk/dcmsr/cmr/cid218.h + dcmsr/include/dcmtk/dcmsr/cmr/cid244.h + dcmsr/include/dcmtk/dcmsr/cmr/cid247.h + dcmsr/include/dcmtk/dcmsr/cmr/cid29.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4020.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4021.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4031.h + dcmsr/include/dcmtk/dcmsr/cmr/cid42.h + dcmsr/include/dcmtk/dcmsr/cmr/cid6147.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7021.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7181.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7445.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7452.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7453.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7464.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7469.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7551.h + dcmsr/libcmr/cid100.cc + dcmsr/libcmr/cid10013.cc + dcmsr/libcmr/cid10033.cc + dcmsr/libcmr/cid11.cc + dcmsr/libcmr/cid218.cc + dcmsr/libcmr/cid244.cc + dcmsr/libcmr/cid247.cc + dcmsr/libcmr/cid29.cc + dcmsr/libcmr/cid4020.cc + dcmsr/libcmr/cid4021.cc + dcmsr/libcmr/cid4031.cc + dcmsr/libcmr/cid42.cc + dcmsr/libcmr/cid6147.cc + dcmsr/libcmr/cid7021.cc + dcmsr/libcmr/cid7181.cc + dcmsr/libcmr/cid7445.cc + dcmsr/libcmr/cid7452.cc + dcmsr/libcmr/cid7453.cc + dcmsr/libcmr/cid7464.cc + dcmsr/libcmr/cid7469.cc + dcmsr/libcmr/cid7551.cc + +- Updated code definitions for DICOM 2025e: + Updated automatically generated code definitions for coding schemes + "DCM", "NCIt" and "UMLS" based on the latest release of the DICOM + standard. + Affects: dcmsr/include/dcmtk/dcmsr/codes/dcm.h + dcmsr/include/dcmtk/dcmsr/codes/ncit.h + dcmsr/include/dcmtk/dcmsr/codes/umls.h + +- Updated data dictionary for DICOM 2025e: + Updated data dictionary for the latest release of the DICOM standard, + although there were no changes (compared to the previous release). + Affects: dcmdata/data/dicom.dic + dcmdata/include/dcmtk/dcmdata/dcdeftag.h + dcmdata/libsrc/dcdictbi.cc + +**** Changes from 2025.11.17 (riesmeier) + +- Made use of getDestinationEncoding(): + Call getDestinationEncoding() method instead of using the member + variable DestinationEncoding directly. This allows for overwriting + the destination encoding in a derived class. + Thanks to Mathieu Malaterre for the + proposal and use case. + Affects: dcmdata/libsrc/dcspchrs.cc + +**** Changes from 2025.11.15 (eichelberg) + +- Fixed minor warning on C++20 build. + Affects: dcmnet/apps/storescp.cc + +**** Changes from 2025.11.14 (eichelberg) + +- Added storescp option --max-associations: + Added a new command line option --max-associations to storescp that allows + the user to limit the number of network associations handled in parallel + when storescp is running in --fork mode. When the maximum number of child + processes is running, further incoming requests are kept on the listen + backlog until either a child process has ended or the request times out. + On Posix systems, this function uses the SIGCHLD signal to keep track of + child processes. On Windows, a list of child processes together with + RegisterWaitForSingleObject() is used instead. + Affects: dcmnet/apps/storescp.cc + dcmnet/docs/storescp.man + +**** Changes from 2025.11.13 (eichelberg) + +- Added callback parameter to "request fork" function: + Added an optional callback function pointer as a parameter to + DUL_requestForkOnTransportConnectionReceipt(). The callback will + only be used on Windows and on that platform will pass a handle + to the child process that has been created in the network module + to serve the incoming network assocation. The handle can be + used to maintain a list and count of active child processes + on Windows, where no SIGCHLD signal is available that automatically + notifies the parent process when a child process has ended. + Affects: dcmnet/include/dcmtk/dcmnet/dul.h + dcmnet/libsrc/dul.cc + +**** Changes from 2025.11.07 (eichelberg) + +- Fixed man page on Windows. + Affects: dcmdata/docs/img2dcm.man + +- Fixed man page on Windows. + Affects: dcmdata/docs/dcm2json.man + +**** Changes from 2025.11.03 (riesmeier) + +- Moved new regression tests to another file. + Affects: dcmdata/tests/tchval.cc + dcmdata/tests/tests.cc + dcmdata/tests/tvrdatim.cc + +- Fixed issue with VR scanner for "DateTime" values: + Fixed an issue with the VR scanner for an incorrect definition of + "DateTime" (DT) values: The UTC suffix "+0000" was not supported, + which was quite surprising since this is the current timezone for + for the UK, for example. + Also added a new regression test to check whether the VR scanner + correctly accepts and rejects certain timezones for DT values. + Thanks to Jez Cooke and Kade Rashid + for the report and suggested fix. + Affects: dcmdata/libsrc/vrscanl.c + dcmdata/libsrc/vrscanl.l + dcmdata/tests/tests.cc + dcmdata/tests/tvrdatim.cc + +- Fixed source code formatting. + Affects: dcmdata/include/dcmtk/dcmdata/dcvrae.h + dcmnet/apps/dcmrecv.cc + dcmnet/apps/movescu.cc + dcmnet/apps/storescp.cc + dcmnet/docs/dcmrecv.man + dcmqrdb/apps/dcmqrscp.cc + dcmwlm/apps/wlcefs.cc + +**** Changes from 2025.11.02 (eichelberg) + +- Added IPv6 support to DCMTK's association acceptors: + Added support for explicitly selecting the IP protocol version (including + IPv6 and a dual-stack IPv4/v6 mode) to all DCMTK "server" applications + that accept incoming DICOM associations. + Thanks to Sobhita Mercy for this contribution. + This closes GitHub pull request #128. + Affects: dcmnet/apps/dcmrecv.cc + dcmnet/apps/movescu.cc + dcmnet/apps/storescp.cc + dcmnet/docs/dcmrecv.man + dcmnet/docs/movescu.man + dcmnet/docs/storescp.man + dcmnet/include/dcmtk/dcmnet/dul.h + dcmnet/libsrc/dul.cc + dcmpstat/apps/dcmprscp.cc + dcmpstat/apps/dcmpsrcv.cc + dcmpstat/etc/dcmpstat.cfg + dcmpstat/include/dcmtk/dcmpstat/dviface.h + dcmpstat/include/dcmtk/dcmpstat/dvpscf.h + dcmpstat/libsrc/dviface.cc + dcmpstat/libsrc/dvpscf.cc + dcmqrdb/apps/dcmqrscp.cc + dcmqrdb/docs/dcmqrscp.man + dcmqrdb/libsrc/dcmqrcbm.cc + dcmwlm/apps/wlcefs.cc + dcmwlm/docs/wlmscpfs.man + +**** Changes from 2025.10.31 (eichelberg) + +- Added missing includes. + Affects: dcmdata/libsrc/dcdocdec.cc + +**** Changes from 2025.10.30 (eichelberg) + +- Switch stdout to binary on Windows: + Switch stdout to binary mode on Windows when decapsulating + an encapsulated document to stdout, to prevent CR/LF conversion. + Affects: dcmdata/libsrc/dcdocdec.cc + +**** Changes from 2025.10.29 (eichelberg) + +- Fixed minor warnings. + Affects: dcmdata/libsrc/dcjsonrd.cc + +**** Changes from 2025.10.28 (eichelberg) + +- DcmJSONReader::clear() now prevents double delete: + DcmJSONReader::clear() now sets jsonDataset_ and tokenArray_ to NULL after + deletion to prevent double-delete when the object is reused. + Thanks to GitHub user "Oss-Auditor" for the pull request. + This closes GitHub PR #131. + Affects: dcmdata/libsrc/dcjsonrd.cc + +**** Changes from 2025.10.25 (riesmeier) + +- Fixed issues with new command line tool dcmdecap. + Affects: dcmdata/apps/Makefile.in + +- Added new tools dcmdecap and dcmencap. + Affects: .gitignore + +**** Changes from 2025.10.25 (eichelberg) + +- Fix TCP initialization error message: + This fixes the following misleading error message that is printed + on Windows when connect() or select() fails: + "TCP Initialization Error: No error". + Thanks to Github user "luk1337" for the pull request + This closes Github pull request #129. + Affects: dcmnet/libsrc/dulfsm.cc + +- Added dcmdecap and dcmencap to module documentation. + Affects: dcmdata/docs/dcmdata.dox + +- Added new tool dcmdecap that extracts encapsulated documents: + Added new tool dcmencap that extracts encapsulated documents into + separate files. The formerly separate tools dcm2pdf and dcm2cda are + now deprecated and act as stubs that call dcmdecap. + Added: dcmdata/apps/dcmdecap.cc + dcmdata/docs/dcmdecap.man + dcmdata/include/dcmtk/dcmdata/dcdocdec.h + dcmdata/libsrc/dcdocdec.cc + Affects: dcmdata/apps/CMakeLists.txt + dcmdata/apps/Makefile.in + dcmdata/apps/dcm2cda.cc + dcmdata/apps/dcm2pdf.cc + dcmdata/docs/dcm2cda.man + dcmdata/docs/dcm2pdf.man + dcmdata/include/dcmtk/dcmdata/dcerror.h + dcmdata/libsrc/CMakeLists.txt + dcmdata/libsrc/Makefile.in + dcmdata/libsrc/dcerror.cc + +**** Changes from 2025.10.24 (eichelberg) + +- Fixed dcmAcceptOddAttributeLength implementation: + The global dcmdata setting 'dcmAcceptOddAttributeLength' determines how + illegal data elements with odd length are handled when reading. + By default, DCMTK accepts odd-length elements and silently pads + them to even length. If this setting is set to false, DCMTK instead + assumes that the attribute value is in fact even length and that + only the value of the length field is wrong, and increases the + length field by 1. This behaviour, which is not the default, + did not work correctly for elements larger than 4 kBytes, which are + skipped when reading from file and only loaded on demand later. + This has now been fixed. + Affects: dcmdata/libsrc/dcelem.cc + +**** Changes from 2025.10.17 (riesmeier) + +- Made DcmItem::updateSpecificCharacterSet() public: + The DcmItem::updateSpecificCharacterSet() method has been made public, + so that it can be called directly if needed (which is only true in + special use cases). + Thanks to Mathieu Malaterre for the + original report. + Affects: dcmdata/include/dcmtk/dcmdata/dcitem.h + dcmdata/include/dcmtk/dcmdata/dcspchrs.h + +**** Changes from 2025.10.16 (riesmeier) + +- Made sure not to use incompatible options: + Made sure that the options --ignore-mlut-depth and --ignore-vlut-depth + are not used together with --check-lut-depth, as they are mutually + exclusive. + Affects: dcmapps/include/dcmtk/dcmapps/dcm2img.h + +- Removed last (empty) entry from UID name map: + Removed the last (empty) entry from the central UID name mapping array + because it serves no purpose. + Thanks to DCMTK forum user "Shaeto" for the original report. + Affects: dcmdata/libsrc/dcuid.cc + +- Warn if Photometric Interpretation is missing: + Just warn if Photometric Interpretation is missing, when ACR-NEMA + compatibility is enabled. + Affects: dcmimgle/libsrc/didocu.cc + +**** Changes from 2025.10.07 (riesmeier) + +- Updated code definitions for DICOM 2025d: + Updated automatically generated code definitions for coding scheme "DCM". + For the coding schemes "NCIt" and "UMLS", there were no changes. + Affects: dcmsr/include/dcmtk/dcmsr/codes/dcm.h + dcmsr/include/dcmtk/dcmsr/codes/ncit.h + dcmsr/include/dcmtk/dcmsr/codes/umls.h + +- Updated data dictionary for DICOM 2025d: + Updated data dictionary for the latest edition of the DICOM standard. + Affects: dcmdata/data/dicom.dic + dcmdata/include/dcmtk/dcmdata/dcdeftag.h + dcmdata/libsrc/dcdictbi.cc + +**** Changes from 2025.09.25 (eichelberg) + +- Fixed minor warning. + Affects: dcmqrdb/include/dcmtk/dcmqrdb/dcmqrcbm.h + +**** Changes from 2025.09.24 (eichelberg) + +- Enabled the use of TLS for sub-associations: + Enabled the use of TLS for storage sub-associations in dcmqrscp, + which completes the TLS support for the C-MOVE retrieve service. + Thanks to Nikolai Beck (GitHub user nbeck-SMT) for the pull request. + This closes GitHub PR #127. + Affects: dcmqrdb/include/dcmtk/dcmqrdb/dcmqrcbm.h + dcmqrdb/libsrc/dcmqrcbm.cc + dcmqrdb/libsrc/dcmqrsrv.cc + +- Added JPEG decoder option for preserving BitsStored: + Added a codec parameter and corresponing command line options in dcmdjpeg + and dcm2img that allow the value of Bits Stored to be preserved even if + the value in the JPEG bitstream is smaller than the value in the DICOM header. + This may help with correctly decoding some defective DICOM images. + This closes DCMTK issue #1054. + Affects: dcmapps/docs/dcm2img.man + dcmapps/include/dcmtk/dcmapps/dcm2img.h + dcmjpeg/apps/dcmdjpeg.cc + dcmjpeg/docs/dcmdjpeg.man + dcmjpeg/include/dcmtk/dcmjpeg/djcparam.h + dcmjpeg/include/dcmtk/dcmjpeg/djdecode.h + dcmjpeg/libsrc/djcodecd.cc + dcmjpeg/libsrc/djcparam.cc + dcmjpeg/libsrc/djdecode.cc + +**** Changes from 2025.09.19 (eichelberg) + +- Fixed off-by-one buffer overrun. + Affects: dcmdata/libsrc/dcencdoc.cc + +**** Changes from 2025.09.18 (eichelberg) + +- Fixed inconsistencies in command line options. + Affects: dcmdata/docs/dcmencap.man + dcmdata/libsrc/dcencdoc.cc + +- Fixed minor warnings. + Affects: dcmdata/libsrc/dcencdoc.cc + ofstd/libsrc/ofstub.cc + +**** Changes from 2025.09.17 (eichelberg) + +- Updated man pages for deprecated tools. + Affects: dcmdata/docs/cda2dcm.man + dcmdata/docs/pdf2dcm.man + dcmdata/docs/stl2dcm.man + +- Made depreciation warnings more prominent. + Affects: ofstd/libsrc/ofstub.cc + +**** Changes from 2025.09.17 (onken) + +- Fix reusability of SCP threads in pool: + Added more debug messages. Fixed some typos in TLS pool test case and + classes and make use OFStandard instead of custom methods. + Affects: dcmnet/include/dcmtk/dcmnet/scppool.h + dcmnet/include/dcmtk/dcmnet/scpthrd.h + dcmnet/libsrc/scppool.cc + dcmnet/libsrc/scpthrd.cc + dcmtls/tests/tscuscptls.cc + +**** Changes from 2025.09.17 (eichelberg) + +- Minor improvements for dcmencap: + The command line options for device data can now also be used when + encapsulating PDF and CDA documents, where the corresponding attributes + are optional. Also improved some warning/error messages. + Affects: dcmdata/docs/dcmencap.man + dcmdata/libsrc/dcencdoc.cc + +- Added consistency check for --series-from option: + Added consistency check for dcmencap's --series-from option to make sure + that series with different Modality values do not get merged. + Furthermore, fixed an inconsistency in the man page. + Affects: dcmdata/docs/dcmencap.man + dcmdata/include/dcmtk/dcmdata/dcencdoc.h + dcmdata/libsrc/dcencdoc.cc + +- Added new tool dcmencap that encapsulates documents: + Added new tool dcmencap that encapsulates documents of all types currently + supported in DICOM (PDF, CDA, STL, MTL and OBJ) into their corresponding + Encapulated Document Storage SOP Class in DICOM. The formerly separate tools + pdf2dcm, cda2dcm and stl2dcm are now deprecated and act as stubs that call + dcmencap. The document format (and, thus, SOP class) is now automatically + determined by default. + Added: dcmdata/apps/dcmencap.cc + dcmdata/docs/dcmencap.man + Affects: dcmdata/apps/CMakeLists.txt + dcmdata/apps/Makefile.dep + dcmdata/apps/Makefile.in + dcmdata/apps/cda2dcm.cc + dcmdata/apps/pdf2dcm.cc + dcmdata/apps/stl2dcm.cc + dcmdata/include/dcmtk/dcmdata/dcencdoc.h + dcmdata/libsrc/dcencdoc.cc + +**** Changes from 2025.09.15 (onken) + +- Fixed leak in SCP Pool class, re-use threads: + In the old implementation busy worker threads have been joined but not + deleted or re-used. + The updated implementation re-uses busy workers by putting them back + into the pool of idle threads. All threads (if ever busy or not) are now + joined and deleted when the pool is being destroyed. + The test has been expanded to use a more thread-safe approach on result + variables and to also tests thread-reuse for threads that had been put + back into the idle pool before. + Thanks to Chuang Zhao for the report and + analysis. + Affects: dcmnet/libsrc/scppool.cc + dcmnet/tests/tpool.cc + +**** Changes from 2025.09.10 (riesmeier) + +- Fixed typo in comment: + Thanks to GitHub user "mrbean-bremen" for the report (see PR #124). + Affects: .github/workflows/cmake-win.yml + +**** Changes from 2025.09.08 (riesmeier) + +- Added check of "writeMode" to write() method: + Added check of "writeMode" parameter to DcmFileFormat::write() method, + so that only a dataset (without meta header) is written if its value + is EWM_dataset, i.e. if requested by the caller. This behavior is now + consistent with the DcmFileFormat::saveFile() method. + Thanks to Mathieu Malaterre for the + original report on this issue. + Affects: dcmdata/libsrc/dcdatset.cc + dcmdata/libsrc/dcfilefo.cc + +**** Changes from 2025.09.03 (riesmeier) + +- Converted array index to unsigned integer value: + Converted array index to unsigned integer value to avoid warning message + reported by gcc 14 on Solaris, e.g. with -Wall or -Wchar-subscripts: + "warning: array subscript has type 'char'" + Also slightly restructured code to make it more readable. + Affects: dcmimage/include/dcmtk/dcmimage/diybrpxt.h + +**** Changes from 2025.08.29 (onken) + +- Replace old-style casts to avoid compiler warnings. + Affects: dcmiod/include/dcmtk/dcmiod/iodtypes.h + dcmiod/tests/tpalette.cc + +**** Changes from 2025.08.28 (onken) + +- Fix linker errors with dupl. method definitions. + Affects: dcmiod/include/dcmtk/dcmiod/iodtypes.h + dcmiod/libsrc/iodtypes.cc + +**** Changes from 2025.08.25 (onken) + +- Fix another data type size warning. + Affects: dcmfg/libsrc/fginterface.cc + +**** Changes from 2025.08.22 (eichelberg) + +- Set implementation information at runtime: + Added APIs that allow the user to set the Implementation Class UID and + Implementation Version name at runtime while writing a DICOM file, + processing an incoming network association request or configuring + and outgoing network association request. + Thanks to Vasyl Horbatenko (GitHub user doskachok) for the pull request. + This closes GitHub PR #68 and #69. + Affects: dcmdata/include/dcmtk/dcmdata/dcfilefo.h + dcmdata/libsrc/dcfilefo.cc + dcmnet/include/dcmtk/dcmnet/assoc.h + dcmnet/include/dcmtk/dcmnet/scpcfg.h + dcmnet/include/dcmtk/dcmnet/scu.h + dcmnet/libsrc/assoc.cc + dcmnet/libsrc/scp.cc + dcmnet/libsrc/scpcfg.cc + dcmnet/libsrc/scppool.cc + dcmnet/libsrc/scu.cc + +**** Changes from 2025.08.22 (riesmeier) + +- Slightly enhanced new functionality of LUT class: + Slightly enhanced new functionality of the LUT class and fixed some + issues introduced with a recent commit (71567ae84). + Affects: dcmimgle/include/dcmtk/dcmimgle/diluptab.h + dcmimgle/libsrc/diluptab.cc + +**** Changes from 2025.08.22 (onken) + +- Remove superfluous comment line. + Affects: .gitignore + +- Restore old GitHub actions rules. + Affects: .github/workflows/cmake-win.yml + +- Use space between template angle brackets. + Affects: dcmfg/include/dcmtk/dcmfg/fginterface.h + dcmfg/libsrc/fginterface.cc + dcmseg/include/dcmtk/dcmseg/segdoc.h + +- Remove test app. + Affects: dcmseg/CMakeLists.txt + Removed: dcmseg/apps/CMakeLists.txt + dcmseg/apps/check_segs.md + dcmseg/apps/check_segs.sh + dcmseg/apps/segdigest.cc + +**** Changes from 2025.08.21 (onken) + +- Fix debug output. + Affects: dcmiod/include/dcmtk/dcmiod/iodtypes.h + +- Remove override specificier: + Marking methods with "override" is only perrmitted + in C+11 or later. + Affects: dcmect/include/dcmtk/dcmect/enhanced_ct.h + dcmfg/include/dcmtk/dcmfg/fginterface.h + dcmiod/include/dcmtk/dcmiod/iodimage.h + dcmpmap/include/dcmtk/dcmpmap/dpmparametricmapiod.h + dcmseg/include/dcmtk/dcmseg/segdoc.h + +- Fix some compiler warnings. + Affects: dcmfg/include/dcmtk/dcmfg/fginterface.h + dcmfg/libsrc/fginterface.cc + dcmseg/include/dcmtk/dcmseg/segutils.h + +- Added Labelmap Segmentation Support: + Added support for creating, writing and reading Labelmap Segmentation + Storage objects to the DCMTK dcmseg module. This also requires an update + of the underlying DCMTK dcmfg (functional group) and dcmiod (common iod + support) modules (libraries), e.g. to add new DICOM modules (such as the + Palette Color Lookup Table module) and to generalize pixel data handling + to support 16 bit pixel data in segmentations. This also led to updates + in related "higher" DCMTK modules like such as dcmect. + Furthermore, reading and writing segmentation objects now can make use + of threads in order to write the per-frame functional group information + which can lead to large performance gains in case the object contains + thousands of frames. The feature has been built into the dcmfg module + and by default, is turned off (i.e. only a single thread is used). One + reason is that DCMTK does not support the C++ "" API and thus + cannot determine the number of available CPU cores on its own. Thus, the + user has to enable the feature when using the dcmfg (FGInterface) or + more for segmentations, the DcmSegmentation API. Other objects like the + Enhanced CT objects within DCMTK's dcmect module can also make use the + the threaded API, but the related dcmfg feature is not yet exposed to + their public APIs. To see how the threaded approach can be used, look at + dcmseg/tests/tbigdim.cc which reads and writes per-frame functional + groups using 16 threads (without checking the available cores of the + system). + Furthermore, a few bugs have been fixed, and minor enhancements have + been added. + Added: dcmiod/include/dcmtk/dcmiod/modiccprofile.h + dcmiod/include/dcmtk/dcmiod/modpalettecolorlut.h + dcmiod/libsrc/modiccprofile.cc + dcmiod/libsrc/modpalettecolorlut.cc + dcmiod/tests/ticcprofile.cc + dcmiod/tests/tpalette.cc + dcmseg/apps/CMakeLists.txt + dcmseg/apps/check_segs.md + dcmseg/apps/check_segs.sh + dcmseg/apps/segdigest.cc + dcmseg/tests/tlabelmap.cc + dcmseg/tests/tpacking.cc + ofstd/include/dcmtk/ofstd/diag/vsconstexp.def + Affects: .github/workflows/cmake-win.yml + .gitignore + ANNOUNCE + CMake/GenerateDCMTKConfigure.cmake + Makefile + config/modules + dcmdata/apps/Makefile.dep + dcmdata/apps/cda2dcm.cc + dcmdata/libdcxml/Makefile.dep + dcmdata/libi2d/Makefile.dep + dcmdata/libsrc/Makefile.dep + dcmdata/tests/Makefile.dep + dcmect/include/dcmtk/dcmect/enhanced_ct.h + dcmect/libsrc/enhanced_ct.cc + dcmect/tests/t_huge_concat.cc + dcmfg/include/dcmtk/dcmfg/concatenationcreator.h + dcmfg/include/dcmtk/dcmfg/concatenationloader.h + dcmfg/include/dcmtk/dcmfg/fg.h + dcmfg/include/dcmtk/dcmfg/fgderimg.h + dcmfg/include/dcmtk/dcmfg/fginterface.h + dcmfg/include/dcmtk/dcmfg/fgseg.h + dcmfg/include/dcmtk/dcmfg/fgtypes.h + dcmfg/libsrc/Makefile.dep + dcmfg/libsrc/concatenationcreator.cc + dcmfg/libsrc/concatenationloader.cc + dcmfg/libsrc/fg.cc + dcmfg/libsrc/fgderimg.cc + dcmfg/libsrc/fginterface.cc + dcmfg/libsrc/fgseg.cc + dcmfg/libsrc/fgtypes.cc + dcmfg/tests/Makefile.dep + dcmfg/tests/t_concatenation_loader.cc + dcmimage/libsrc/Makefile.dep + dcmimgle/include/dcmtk/dcmimgle/diluptab.h + dcmimgle/libsrc/Makefile.dep + dcmimgle/libsrc/diluptab.cc + dcmiod/include/dcmtk/dcmiod/iodimage.h + dcmiod/include/dcmtk/dcmiod/iodtypes.h + dcmiod/include/dcmtk/dcmiod/iodutil.h + dcmiod/include/dcmtk/dcmiod/modbase.h + dcmiod/include/dcmtk/dcmiod/modgeneralimage.h + dcmiod/include/dcmtk/dcmiod/modimagepixelvariant.h + dcmiod/libsrc/CMakeLists.txt + dcmiod/libsrc/Makefile.dep + dcmiod/libsrc/Makefile.in + dcmiod/libsrc/iodmacro.cc + dcmiod/libsrc/iodtypes.cc + dcmiod/libsrc/iodutil.cc + dcmiod/libsrc/modmultiframedimension.cc + dcmiod/tests/CMakeLists.txt + dcmiod/tests/Makefile.dep + dcmiod/tests/Makefile.in + dcmiod/tests/tchecks.cc + dcmiod/tests/tests.cc + dcmjpeg/libsrc/Makefile.dep + dcmnet/apps/Makefile.dep + dcmpmap/include/dcmtk/dcmpmap/dpmparametricmapiod.h + dcmpmap/libsrc/dpmparametricmapiod.cc + dcmpstat/apps/Makefile.dep + dcmpstat/libsrc/Makefile.dep + dcmseg/CMakeLists.txt + dcmseg/include/dcmtk/dcmseg/segdoc.h + dcmseg/include/dcmtk/dcmseg/segment.h + dcmseg/include/dcmtk/dcmseg/segtypes.h + dcmseg/include/dcmtk/dcmseg/segutils.h + dcmseg/libsrc/Makefile.dep + dcmseg/libsrc/segdoc.cc + dcmseg/libsrc/segment.cc + dcmseg/libsrc/segtypes.cc + dcmseg/libsrc/segutils.cc + dcmseg/tests/CMakeLists.txt + dcmseg/tests/Makefile.dep + dcmseg/tests/Makefile.in + dcmseg/tests/tbigdim.cc + dcmseg/tests/tconcat_binary.cc + dcmseg/tests/tests.cc + dcmseg/tests/troundtrip.cc + dcmseg/tests/tutils.cc + dcmsr/libsrc/Makefile.dep + dcmtls/libsrc/tlsfmacr.h + dcmtract/include/dcmtk/dcmtract/trctrackset.h + oficonv/libsrc/Makefile.dep + ofstd/include/dcmtk/ofstd/ofdiag.h + ofstd/libsrc/Makefile.dep + ofstd/tests/Makefile.dep + +**** Changes from 2025.08.21 (eichelberg) + +- Added method for accessing a DICOM file preamble: + Added a method that allows the user to access the file preamble after reading + a DICOM file. + This closes DCMTK issue #185. + Affects: dcmdata/include/dcmtk/dcmdata/dcmetinf.h + dcmdata/libsrc/dcmetinf.cc + +**** Changes from 2025.08.19 (eichelberg) + +- Improved CMake code for static builds: + The variables containing the lists of additional libraries that need to be + linked when a certain static library is linked (such as + LIBPNG_EXTRA_LIBS_STATIC) are now also taken into account on Windows. + Furthermore, added some improvements for static builds on Debian Linux. + Thanks to Khang Trần for the pull request. + This closes DCMTK PR #104. + Affects: CMake/3rdparty.cmake + CMake/dcmtkPrepare.cmake + +- Fixed sample code: + Added proper null termination to string in sample program. + Thanks to Mathieu Malaterre for the pull request. + This closes GitHub PR #123. + Affects: oficonv/docs/oficonv.dox + +- Link libnsl and libsocket only when required: + DCMTK no longer unconditionally tests or links against `nsl` and `socket`. + These libraries are now only considered on pre-11.4 Solaris systems, + where functions such as `gethostbyname` were historically provided + outside libc. This avoids dependencies on libraries that may directly + or indirectly pull in a different OpenSSL version. + Thanks to Jean-Christophe Fillion-Robin + for the pull request. + This closes GitHub PR #125. + Affects: CMake/dcmtkPrepare.cmake + +**** Changes from 2025.08.19 (riesmeier) + +- Reduced number of warnings (gcc -Wconversion): + Reduced number of warnings reported by gcc with extra warning option + -Wconversion. + Affects: dcmimgle/include/dcmtk/dcmimgle/diinpxt.h + +**** Changes from 2025.08.18 (riesmeier) + +- Fixed warning reported by Visual Studio. + Affects: dcmimgle/include/dcmtk/dcmimgle/diinpxt.h + +- Fixed off-by-one error when processing an image: + Fixed an off-by-one error when processing an unusual encoding of a DICOM + image, e.g. with partial access to the pixel data. The internal handling + of the input pixel data with a certain combination of VR, Bits Allocated + and Bits Stored (see "case 2c") was missing the final pixel value (for + images with an odd number of bytes) in size and could, therefore, result + in an undefined behavior (incl. an out-of-bounds write to a memory block + on the heap). + Thanks to drak for the report and sample file. + Affects: dcmimgle/include/dcmtk/dcmimgle/diinpxt.h + +- Fixed issue with commit 7ad81d69b: + Fixed an issue with recently committed changes that fix a problem with + invalid YBR_FULL images + Affects: dcmimage/include/dcmtk/dcmimage/diybrpxt.h + +**** Changes from 2025.08.18 (eichelberg) + +- Fixed metaheader generation in dcmodify and dcmconv: + The dcmodify and dcmconv tools incorrectly changed the MediaStorageSOPClassUID + and MediaStorageSOPInstanceUID in the DICOM meta-header when processing + DICOMDIR files. This was caused by the EWM_createNewMeta write mode, + which was introduced as the default behavior for DcmFileFormat::saveFile() + in DCMTK 3.6.6. This option deleted and re-created the entire meta-header + before writing the file. When the SOP Class UID and the SOP Instance UID + were not present in the main dataset, a private SOP class with a + newly generated SOP Instance UID were written. This is now fixed: + MediaStorageSOPClassUID and MediaStorageSOPInstanceUID are preserved + when not present in the main dataset. + Furthermore, dcmodify's "--no-meta-uid" command line option, which was + apparently broken since DCMTK 3.6.6, is now also fixed. + This closes DCMTK issue #1148. + Affects: dcmdata/apps/mdfdsman.cc + dcmdata/include/dcmtk/dcmdata/dctypes.h + dcmdata/libsrc/dcfilefo.cc + +- Added note on character set conversion: + Added a note in the API documentation pointing out that the character set + conversion code will not perform a thorough validation of the input strings, + i.e. characters that are permitted in the source character set but forbidden + in DICOM will be converted without warning or error. + Affects: dcmdata/include/dcmtk/dcmdata/dcitem.h + dcmdata/include/dcmtk/dcmdata/dcspchrs.h + +**** Changes from 2025.08.15 (riesmeier) + +- Fixed issue with invalid "YBR_FULL" DICOM images: + Fixed an issue when processing an invalid DICOM image with a Photometric + Interpretation of "YBR_FULL" and a Planar Configuration of "1" where + the number of pixels stored does not match the expected number of pixels + (much too less). Now, the pixel data of such an image is not processed + at all, but an empty image (black pixels) is created instead. The user + is warned about this by an appropriate log message. + Thanks to Ding zhengzheng for the report + and the sample file (PoC). + Affects: dcmimage/include/dcmtk/dcmimage/dicopxt.h + dcmimage/include/dcmtk/dcmimage/diybrpxt.h + dcmimgle/libsrc/dcmimage.cc + +**** Changes from 2025.08.11 (eichelberg) + +- Improved documentation of initializeXfer() method. + Affects: dcmdata/include/dcmtk/dcmdata/dcdatset.h + +- Initialize dataset transfer syntax when reading file: + Added code to initialize the transfer syntax of the dataset component + when reading a DICOM file (DcmFileFormat), in order to make sure that + the right transfer syntax will be reported even if the dataet is empty. + This closes DCMTK issue #1159. + Affects: dcmdata/include/dcmtk/dcmdata/dcdatset.h + dcmdata/libsrc/dcdatset.cc + dcmdata/libsrc/dcfilefo.cc + +**** Changes from 2025.08.05 (eichelberg) + +- Moved pragma to separate include file: + Moved a pragma needed for compilation with gcc on MacOS + into a separate include file referenced by the macro + DCMTK_DIAGNOSTIC_IGNORE_CLANG_PRAGMAS_ON_GCC. + Added: ofstd/include/dcmtk/ofstd/diag/clangprg.def + Affects: dcmdata/include/dcmtk/dcmdata/dcmxml/xml2dcm.h + dcmdata/libdcxml/xml2dcm.cc + dcmsr/apps/xml2dsr.cc + dcmsr/include/dcmtk/dcmsr/dsrxmlc.h + ofstd/include/dcmtk/ofstd/ofdiag.h + +**** Changes from 2025.08.04 (eichelberg) + +- Added method for activating the list of ciphersuites: + Added a method for activating the list of ciphersuites defined through + DcmTLSSCU::setTLSProfile() and DcmTLSSCU::addCipherSuite(). Previously + this list was never actually activated. + Thanks to Ben Chen for the bug report and patch. + Affects: dcmtls/include/dcmtk/dcmtls/tlsscu.h + dcmtls/libsrc/tlsscu.cc + +**** Changes from 2025.08.03 (eichelberg) + +- Added missing include on Windows. + Affects: dcmdata/libsrc/dcjsonrd.cc + +**** Changes from 2025.08.02 (eichelberg) + +- Added support for file BulkDataURIs in json2dcm: + Added support for reading bulk data from files in the local filesystem + when converting a dataset in DICOM JSON encoding to a DICOM file. + Affects: dcmdata/apps/json2dcm.cc + dcmdata/docs/json2dcm.man + dcmdata/include/dcmtk/dcmdata/dcjsonrd.h + dcmdata/libsrc/dcjsonrd.cc + +**** Changes from 2025.07.31 (eichelberg) + +- Fixed OFStandard API documentation: + Removed reference to big endian byte order in Base64 encoder and + decoder methods in class OFStandard since this does not apply to DICOM. + Affects: ofstd/include/dcmtk/ofstd/ofstd.h + +**** Changes from 2025.07.24 (eichelberg) + +- Added error code for unsupported URI type. + Affects: dcmdata/include/dcmtk/dcmdata/dcerror.h + dcmdata/libsrc/dcerror.cc + +- Added default for bulk data size in help output. + Affects: dcmdata/apps/dcm2json.cc + dcmdata/docs/dcm2json.man + +**** Changes from 2025.07.16 (riesmeier) + +- Updated Context Group classes for DICOM 2025c: + Updated automatically generated Context Group classes for the latest + edition of the DICOM standard. All supported classes were updated, + even though there were no changes to most of them. + Affects: dcmsr/include/dcmtk/dcmsr/cmr/cid100.h + dcmsr/include/dcmtk/dcmsr/cmr/cid10013.h + dcmsr/include/dcmtk/dcmsr/cmr/cid10033.h + dcmsr/include/dcmtk/dcmsr/cmr/cid11.h + dcmsr/include/dcmtk/dcmsr/cmr/cid218.h + dcmsr/include/dcmtk/dcmsr/cmr/cid244.h + dcmsr/include/dcmtk/dcmsr/cmr/cid247.h + dcmsr/include/dcmtk/dcmsr/cmr/cid29.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4020.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4021.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4031.h + dcmsr/include/dcmtk/dcmsr/cmr/cid42.h + dcmsr/include/dcmtk/dcmsr/cmr/cid6147.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7021.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7181.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7445.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7452.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7453.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7464.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7469.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7551.h + dcmsr/libcmr/cid100.cc + dcmsr/libcmr/cid10013.cc + dcmsr/libcmr/cid10033.cc + dcmsr/libcmr/cid11.cc + dcmsr/libcmr/cid218.cc + dcmsr/libcmr/cid244.cc + dcmsr/libcmr/cid247.cc + dcmsr/libcmr/cid29.cc + dcmsr/libcmr/cid4020.cc + dcmsr/libcmr/cid4021.cc + dcmsr/libcmr/cid4031.cc + dcmsr/libcmr/cid42.cc + dcmsr/libcmr/cid6147.cc + dcmsr/libcmr/cid7021.cc + dcmsr/libcmr/cid7181.cc + dcmsr/libcmr/cid7445.cc + dcmsr/libcmr/cid7452.cc + dcmsr/libcmr/cid7453.cc + dcmsr/libcmr/cid7464.cc + dcmsr/libcmr/cid7469.cc + dcmsr/libcmr/cid7551.cc + +- Updated code definitions for DICOM 2025c: + Updated automatically generated code definitions for coding schemes "DCM" + and "UMLS". For the coding scheme "NCIt", there were no changes. + Affects: dcmsr/include/dcmtk/dcmsr/codes/dcm.h + dcmsr/include/dcmtk/dcmsr/codes/ncit.h + dcmsr/include/dcmtk/dcmsr/codes/umls.h + +- Updated data dictionary for DICOM 2025c: + Updated data dictionary for the latest edition of the DICOM standard, + which has been released only recently. + Affects: dcmdata/data/dicom.dic + dcmdata/include/dcmtk/dcmdata/dcdeftag.h + dcmdata/libsrc/dcdictbi.cc + +**** Changes from 2025.07.12 (riesmeier) + +- Replaced tab characters by spaces. + Affects: dcmdata/include/dcmtk/dcmdata/dcjsonrd.h + dcmdata/libsrc/dcjsonrd.cc + +**** Changes from 2025.07.11 (eichelberg) + +- Refactored DICOM JSON parser into reusable class: + Refactored the DICOM JSON model parser that provides the core functionality + of the json2dcm application into a reusable class in the dcmdata library. + In this process, the code was also significantly revised and refined. + Added: dcmdata/include/dcmtk/dcmdata/dcjsonrd.h + dcmdata/libsrc/dcjsonrd.cc + Affects: dcmdata/apps/json2dcm.cc + dcmdata/libsrc/CMakeLists.txt + dcmdata/libsrc/Makefile.in + +**** Changes from 2025.07.10 (riesmeier) + +- Replaced wrong term in manpage. + Affects: dcmdata/docs/json2dcm.man + +- Fixed various spelling and formatting issues. + Affects: dcmdata/apps/json2dcm.cc + dcmdata/docs/json2dcm.man + +**** Changes from 2025.07.09 (eichelberg) + +- Fixed warnings from gcc on MacOS 15: + The version of libxml2 provided as part of the MacOS 15 development tools + contain some Clang specific pragmas that cause warnings when compiling + with gcc. These are now suppressed. + Affects: dcmdata/include/dcmtk/dcmdata/dcmxml/xml2dcm.h + dcmdata/libdcxml/xml2dcm.cc + dcmsr/apps/xml2dsr.cc + dcmsr/include/dcmtk/dcmsr/dsrxmlc.h + +- Removed unneccessary parameter. + Affects: dcmdata/apps/json2dcm.cc + +**** Changes from 2025.07.08 (eichelberg) + +- Added options for handling JSON arrays of datasets: + Added command line options for handling JSON arrays of DICOM JSON datasets, + which are returned by DICOMweb services such as QIDO-RS or WADO-RS when + multiple datasets are returned for one request. + Affects: dcmdata/apps/json2dcm.cc + dcmdata/docs/json2dcm.man + +**** Changes from 2025.07.07 (eichelberg) + +- Added --ignore-bulkdata-uri option to json2dcm: + Added a command line option that allows json2dcm to ignore bulk data URIs + in DICOM JSON files, which are not yet supported. Furthermore revised the + logger messages for better consistency and less duplication. + Affects: dcmdata/apps/json2dcm.cc + dcmdata/docs/json2dcm.man + +- Improved json2dcm performance: + Improved json2dcm performance, in particular when reading large look-up + tables. Also improved consistency of logger output. + Affects: dcmdata/apps/json2dcm.cc + +**** Changes from 2025.07.01 (riesmeier) + +- Made some functions virtual to allow overriding: + Made some functions for character set conversion virtual to allow for + overriding them in derived classes. + Thanks to Mathieu Malaterre for the idea. + Affects: dcmdata/include/dcmtk/dcmdata/dcitem.h + dcmdata/include/dcmtk/dcmdata/dcspchrs.h + dcmdata/libsrc/dcitem.cc + +- Do not warn on "Undefind Length" for some VRs: + Do not warn when using "Undefined Length" for VRs that support this + according to DICOM PS3.5, even if it does not occur in practice. + Affects: dcmdata/libsrc/dcitem.cc + +**** Changes from 2025.06.23 (riesmeier) + +- Fixed formatting and replaced wrong term. + Affects: dcmdata/docs/dcm2json.man + +**** Changes from 2025.06.23 (eichelberg) + +- Added note on bulk data export. + Affects: dcmdata/docs/dcm2json.man + +**** Changes from 2025.06.19 (riesmeier) + +- Fixed minor issues in manpage: + Fixed minor issues in manpage, e.g. capitalization, line breaks, + indentation and missing Doxygen markup. + Affects: dcmdata/docs/dcm2json.man + dcmdata/docs/json2dcm.man + +- Fixed indentation of source header. + Affects: dcmdata/apps/dcm2json.cc + +**** Changes from 2025.06.19 (eichelberg) + +- Added a note that json2dcm accepts trailing commas: + Added a note to the manual page explaining that json2dcm will accepts + some invalid JSON datasets, such as datasets with trailing commas, + without error or warning because they are gracefully handled by the + underlying JSON parser. + Thanks to Mathieu Malaterre for the report. + Affects: dcmdata/docs/json2dcm.man + +- Fixed generation of 32-bit BMP on Big Endian CPUs: + Fixed the conversion of DICOM images to 32-bit BMP files, which worked + correctly on Little Endian CPUs but not on Big Endian CPUs. + Affects: dcmimage/include/dcmtk/dcmimage/dicopxt.h + dcmimgle/libsrc/dimoimg.cc + +**** Changes from 2025.06.19 (malaterre) + +- Access JSON bulk data URI prefix through API: + In DcmJsonFormat::writeBulkData(), access the URI prefix for bulk data + URIs through the official API method getBulkDataURIPrefix() in order + to allow derived classes to override the implementation. + Thanks to Mathieu Malaterre for the pull request. + This closes GitHub PR #121. + Affects: dcmdata/libsrc/dcjson.cc + +- Reworked JSON writeBulkData API: + Now exposing the same set of parameters as the caller function + writeBinaryAttribute. + Thanks to Mathieu Malaterre for the pull request. + This closes GitHub PR #122. + Affects: dcmdata/include/dcmtk/dcmdata/dcjson.h + dcmdata/libsrc/dcchrstr.cc + dcmdata/libsrc/dcelem.cc + dcmdata/libsrc/dcjson.cc + dcmdata/libsrc/dcpixel.cc + dcmdata/libsrc/dcvrds.cc + dcmdata/libsrc/dcvrfd.cc + dcmdata/libsrc/dcvrfl.cc + dcmdata/libsrc/dcvris.cc + dcmdata/libsrc/dcvrsv.cc + dcmdata/libsrc/dcvruv.cc + +**** Changes from 2025.06.16 (eichelberg) + +- Reworded comment on HP color transform: + Reworded comment about the non-standard HP color transformation in JPEG-LS. + Thanks to Mathieu Malaterre for the pull request. + This closes GitHub PR #119. + Affects: dcmjpls/libsrc/djcodece.cc + +**** Changes from 2025.06.14 (eichelberg) + +- Fixed minor MSVC warning. + Affects: dcmdata/apps/json2dcm.cc + +**** Changes from 2025.06.13 (xu) + +- Changes to JSON2DCM: Moved Exitcodes to JSON2DCM to ensure fixed codes. Removed some DEBUG testing output. + Affects: dcmdata/apps/json2dcm.cc + +**** Changes from 2025.06.13 (eichelberg) + +- Apply bit mask when passing pixel data to CharLS: + Apply bit mask when passing pixel data to the CharLS JPEG-LS encoder + in "cooked" mode. + Thanks to Mathieu Malaterre for the bug report. + This closes DCMTK Issue #1160. + Affects: dcmjpls/libsrc/djcodece.cc + +**** Changes from 2025.06.13 (onken) + +- Made destructor virtual: + Classes with virtual functions must have their destructor also made + virtual. + Thanks to Mathieu Malaterre for the pull + request. + This closes GitHub pull request #120. + Affects: dcmdata/include/dcmtk/dcmdata/libi2d/i2d.h + +**** Changes from 2025.06.12 (riesmeier) + +- Minor fixes to manpage and syntax usage: + Made the description of the tool "json2dcm" more consistent with the + one of the pre-existing tool "xml2dcm". + Affects: dcmdata/apps/json2dcm.cc + dcmdata/docs/json2dcm.man + +- Changed log level and text of some log messages: + Changed log level of some log messages and slightly modified the text. + Also check the output file for an empty string (similar to "xml2dcm"). + Affects: dcmdata/apps/json2dcm.cc + +- Various minor fixes, mainly related to json2dcm: + Various minor fixes that are mainly related to the new command line tool + json2dcm. For example, spelling errors, wrong capitalization, unused + error and exit codes. Also made the log output more consistent. + Affects: dcmdata/apps/json2dcm.cc + dcmdata/docs/json2dcm.man + dcmdata/include/dcmtk/dcmdata/dcerror.h + dcmdata/libsrc/dcchrstr.cc + dcmdata/libsrc/dcerror.cc + dcmdata/libsrc/dcistrmz.cc + dcmdata/libsrc/dcitem.cc + dcmdata/libsrc/dcpixel.cc + dcmdata/libsrc/dcrleccd.cc + dcmdata/libsrc/dcvrds.cc + dcmdata/libsrc/dcvris.cc + +**** Changes from 2025.06.10 (riesmeier) + +- Fixed wrong sub-group title. + Affects: dcmdata/docs/json2dcm.man + +**** Changes from 2025.06.10 (eichelberg) + +- Updated documentation parameter "dcmfile-out": + Updated documentation of command line parameter "dcmfile-out", + as writing to stdout is supported, but the paramters is not optional + anymore. Also fixed minor issues. + Affects: dcmdata/apps/json2dcm.cc + dcmdata/docs/json2dcm.man + +**** Changes from 2025.06.10 (riesmeier) + +- Added new command line tool "json2dcm". + Affects: .gitignore + +- Added "json2dcm" to module's documentation page. + Affects: dcmdata/docs/dcmdata.dox + +- Updated documentation of parameter "jsonfile-in": + Updated documentation of command line parameter "jsonfile-in" as reading + from stdin is now also supported. Also fixed various other minor issues + related to the documentation. + Affects: dcmdata/apps/json2dcm.cc + dcmdata/docs/json2dcm.man + +**** Changes from 2025.06.09 (eichelberg) + +- Added support for reading from stdin: + Added support in json2dcm for reading the JSON file from stdin if the + input file name is given as "-". Improved character set related warnings. + Affects: dcmdata/apps/json2dcm.cc + +**** Changes from 2025.06.07 (eichelberg) + +- Fixed memory leaks and segfault in json2dcm: + Fixed two memory leaks and a segmentation fault that could occur on some + platforms due to a loop reading past the end of an array. + Affects: dcmdata/apps/json2dcm.cc + +- Replaced C-style casts with C++ casts. + Affects: dcmdata/apps/json2dcm.cc + +- Fixed URL encoding of path separators. + Affects: dcmdata/apps/dcm2json.cc + +- Minor fix in jsmn API definition. + Affects: ofstd/include/dcmtk/ofstd/ofjsmn.h + +**** Changes from 2025.06.06 (eichelberg) + +- Fixed shared library build. + Affects: ofstd/include/dcmtk/ofstd/ofjsmn.h + +**** Changes from 2025.06.06 (xu) + +- Initial release of the json2dcm command line tool: + Initial release of json2dcm, a tool that converts the contents of a + DICOM dataset in JSON encoding to a binary DICOM file or dataset. + The JSON document is expected to conform to the "DICOM JSON Model" + as defined in DICOM Part 18, Section F. In this initial release, + attribute values referenced through BulkDataURIs are not yet supported. + Added: dcmdata/apps/json2dcm.cc + dcmdata/docs/json2dcm.man + Affects: dcmdata/apps/CMakeLists.txt + dcmdata/apps/Makefile.in + dcmdata/include/dcmtk/dcmdata/dcerror.h + dcmdata/libsrc/dcerror.cc + +- Added JSON parser to module ofstd: + Added the "jmsn" header-only JSON parser by Serge Zaitsev to module ofstd. + Added: ofstd/include/dcmtk/ofstd/ofjsmn.h + ofstd/libsrc/ofjsmn.cc + Affects: COPYRIGHT + ofstd/libsrc/CMakeLists.txt + ofstd/libsrc/Makefile.in + +**** Changes from 2025.06.06 (eichelberg) + +- Encode special characters in file: URLs. + Affects: dcmdata/apps/dcm2json.cc + +**** Changes from 2025.06.05 (riesmeier) + +- Enhanced log output for incorrect pixel data: + Enhanced log output for incorrectly encoded encapsulated pixel data. + In particular, when encapsulated pixel data is encoded with a VR of OW + instead of OB, the warning message is now much easier to understand. + Affects: dcmdata/libsrc/dcitem.cc + +- Made output of warning messages more consistent. + Affects: dcmdata/libsrc/dcitem.cc + +**** Changes from 2025.06.01 (onken) + +- Fixed test failing due to new ftoa implementation: + Some months ago DCMTK has introduced a new OFStandard::ftoa + implementation() which leads to slightly different floating + point numbers when converting from a strings. + Affects: dcmfg/tests/t_concatenation_loader.cc + +**** Changes from 2025.06.01 (eichelberg) + +- Fixed bug in dcm2json: + Fixed bug in dcm2json that caused the tool to crash when compiled with STL. + Affects: dcmdata/apps/dcm2json.cc + +**** Changes from 2025.05.30 (onken) + +- Make DCMTK clients build w/o __cplusplus setting: + For its internal C++ standard version testing DCMTK sets the __cplusplus + macro for Visual Studio since some versions don't set it. + However, some external projects that just include the result of this check + (which can be found in DCMTK "main" configuration header osconfig.h), + might build their code without this flag and fail if osconfig.h still + relies on it. + The current patch does not change DCMTK's internal version checking but + makes sure that once built external projects don't stumble over the + __cplusplus macro requirements in osconfig.h. Instead, osconfig.h now + checks for _MSVC_LANG (and __cplusplus if that cannot be found). + Thanks to Jean-Christophe Fillion-Robins who originally came up with the + patch in the the CommonTK (CTK) project. + Affects: CMake/osconfig.h.in + +**** Changes from 2025.05.28 (riesmeier) + +- Added missing GNU Autoconf support to "tests". + Affects: dcmtract/tests/Makefile.dep + dcmtract/tests/Makefile.in + +**** Changes from 2025.05.28 (eichelberg) + +- Fixed JSON conversion of compressed images. + Affects: dcmdata/libsrc/dcpixel.cc + +- Minor fixes in dcm2json. + Affects: dcmdata/apps/dcm2json.cc + dcmdata/libsrc/dcjson.cc + +**** Changes from 2025.05.28 (onken) + +- Fixed compiler error/warning in test. + Affects: dcmtract/tests/tcreate.cc + +**** Changes from 2025.05.27 (onken) + +- Fix writing of Tractography Results Series: + Fix writing of Tractography Results Series which has been omitted in + the past. + Also fixed requirement for Referenced Instance Sequence and added + debug messages. + Added test that demonstrates how to create an Tractography Results + object and is helpful to find problems with the code. + Thanks to forum user "hapap" for the report. + Added: dcmtract/tests/CMakeLists.txt + dcmtract/tests/tcreate.cc + dcmtract/tests/tests.cc + Affects: dcmtract/CMakeLists.txt + dcmtract/include/dcmtk/dcmtract/trctypes.h + dcmtract/libsrc/trcmodtractresults.cc + dcmtract/libsrc/trctrackset.cc + dcmtract/libsrc/trctractographyresults.cc + dcmtract/libsrc/trctypes.cc + +- Use temp filename. + Affects: dcmect/tests/t_overflow.cc + +- Added "component" to debug dump. + Affects: dcmiod/libsrc/iodrules.cc + +**** Changes from 2025.05.27 (eichelberg) + +- Fixed compilation of dcm2json on Windows. + Affects: dcmdata/apps/dcm2json.cc + +**** Changes from 2025.05.23 (eichelberg) + +- Added support for BulkDataURI in JSON export: + Added support for the generation of BulkDataURIs when exporting DICOM files + to JSON in the dcm2json tool. When enabled, bulk data is stored in files + that are expected to be accessible through a WADO-RS service. A user-defined + URI prefix can be specified for this purpose. Since dcm2json is not a complete + DICOMweb implementation, there is no support (yet) for generating MIME multipart + structures with bulk data references formatted as a UUID. + This closes DCMTK Feature #1151. + Affects: dcmdata/apps/dcm2json.cc + dcmdata/docs/dcm2json.man + dcmdata/include/dcmtk/dcmdata/dcerror.h + dcmdata/include/dcmtk/dcmdata/dcjson.h + dcmdata/include/dcmtk/dcmdata/dcpixseq.h + dcmdata/include/dcmtk/dcmdata/dcxfer.h + dcmdata/libsrc/dcchrstr.cc + dcmdata/libsrc/dcelem.cc + dcmdata/libsrc/dcerror.cc + dcmdata/libsrc/dcjson.cc + dcmdata/libsrc/dcpixel.cc + dcmdata/libsrc/dcpixseq.cc + dcmdata/libsrc/dcvrds.cc + dcmdata/libsrc/dcvrfd.cc + dcmdata/libsrc/dcvrfl.cc + dcmdata/libsrc/dcvris.cc + dcmdata/libsrc/dcvrobow.cc + dcmdata/libsrc/dcvrod.cc + dcmdata/libsrc/dcvrof.cc + dcmdata/libsrc/dcvrol.cc + dcmdata/libsrc/dcvrov.cc + dcmdata/libsrc/dcvrsv.cc + dcmdata/libsrc/dcvruv.cc + dcmdata/libsrc/dcxfer.cc + +**** Changes from 2025.05.14 (eichelberg) + +- Fixed linker warning on MacOS. + Affects: dcmnet/apps/CMakeLists.txt + +- Fixed C++98 build. + Affects: dcmdata/libsrc/dcvrobow.cc + +**** Changes from 2025.05.09 (eichelberg) + +- Added TLS support to movescu: + Added TLS support to the movescu tool, both for outgoing + and for incoming associations. + Affects: dcmnet/apps/CMakeLists.txt + dcmnet/apps/Makefile.in + dcmnet/apps/movescu.cc + dcmnet/docs/movescu.man + dcmnet/libsrc/dcmtrans.cc + +- Added TLS support for C-MOVE in dcmqrscp: + The version of dcmqrscp in DCMTK 3.6.9 had partial support for secure + connections in that TLS was supported for incoming associations, + but not for outgoing connections. This means that TLS worked for + storage, query and retrieve with C-GET, but not with C-MOVE. The + TLS support has now be completed to also cover the outgoing + associations required for C-MOVE. + Affects: dcmqrdb/apps/dcmqrscp.cc + dcmqrdb/include/dcmtk/dcmqrdb/dcmqropt.h + dcmqrdb/libsrc/dcmqrcbm.cc + dcmqrdb/libsrc/dcmqropt.cc + +**** Changes from 2025.05.08 (onken) + +- Handle NULL values appropriately: + The compare methods() in DcmOtherByteOtherWord and DcmPolymorphOBOW did + not properly check for NULL values before handing them into memcmp(). + Affects: dcmdata/include/dcmtk/dcmdata/dcvrobow.h + dcmdata/libsrc/dcvrobow.cc + dcmdata/libsrc/dcvrpobw.cc + +**** Changes from 2025.04.16 (riesmeier) + +- Indicated retired attributes in documentation: + Made sure that the recently retired EthnicGroup attribute is documented + as such, both in header files and man pages. + Affects: dcmqrdb/docs/dcmqrscp.man + dcmwlm/docs/wlmscpfs.man + dcmwlm/include/dcmtk/dcmwlm/wlds.h + +- Added support for new DICONDE Storage SOP Class: + Added support for the DICONDE Storage SOP Class for ultrasonic waveforms + introduced with DICOM 2025b (based on a revised version of ASTM E2663). + Affects: dcmdata/include/dcmtk/dcmdata/dcuid.h + dcmdata/libsrc/dcuid.cc + dcmnet/docs/movescu.man + dcmnet/docs/storescp.man + dcmnet/etc/storescp.cfg + dcmnet/etc/storescu.cfg + dcmqrdb/docs/dcmqrscp.man + dcmqrdb/etc/dcmqrprf.cfg + +- Updated Context Group classes for DICOM 2025b: + Updated automatically generated Context Group classes for the latest + edition of the DICOM standard. All supported classes were updated, + even though there were no changes to most of them. + Affects: dcmsr/include/dcmtk/dcmsr/cmr/cid100.h + dcmsr/include/dcmtk/dcmsr/cmr/cid10013.h + dcmsr/include/dcmtk/dcmsr/cmr/cid10033.h + dcmsr/include/dcmtk/dcmsr/cmr/cid11.h + dcmsr/include/dcmtk/dcmsr/cmr/cid218.h + dcmsr/include/dcmtk/dcmsr/cmr/cid244.h + dcmsr/include/dcmtk/dcmsr/cmr/cid247.h + dcmsr/include/dcmtk/dcmsr/cmr/cid29.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4020.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4021.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4031.h + dcmsr/include/dcmtk/dcmsr/cmr/cid42.h + dcmsr/include/dcmtk/dcmsr/cmr/cid6147.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7021.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7181.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7445.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7452.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7453.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7464.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7469.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7551.h + dcmsr/libcmr/cid100.cc + dcmsr/libcmr/cid10013.cc + dcmsr/libcmr/cid10033.cc + dcmsr/libcmr/cid11.cc + dcmsr/libcmr/cid218.cc + dcmsr/libcmr/cid244.cc + dcmsr/libcmr/cid247.cc + dcmsr/libcmr/cid29.cc + dcmsr/libcmr/cid4020.cc + dcmsr/libcmr/cid4021.cc + dcmsr/libcmr/cid4031.cc + dcmsr/libcmr/cid42.cc + dcmsr/libcmr/cid6147.cc + dcmsr/libcmr/cid7021.cc + dcmsr/libcmr/cid7181.cc + dcmsr/libcmr/cid7445.cc + dcmsr/libcmr/cid7452.cc + dcmsr/libcmr/cid7453.cc + dcmsr/libcmr/cid7464.cc + dcmsr/libcmr/cid7469.cc + dcmsr/libcmr/cid7551.cc + +- Updated code definitions for DICOM 2025b: + Updated automatically generated code definitions for coding schemes "DCM" + and "UMLS". For the coding scheme "NCIt", there were no changes. + Affects: dcmsr/include/dcmtk/dcmsr/codes/dcm.h + dcmsr/include/dcmtk/dcmsr/codes/ncit.h + dcmsr/include/dcmtk/dcmsr/codes/umls.h + +- Updated data dictionary for DICOM 2025b: + Updated data dictionary for the latest edition of the DICOM standard, which + has been released only recently. + Since one of the DICOM attributes has been "retired", the name of the + associated DcmTagKey constant also changed. This required minor changes to + the source code of the dcmiod, dcmqrdb, dcmrt and dcmwlm module. + Affects: dcmdata/data/dicom.dic + dcmdata/include/dcmtk/dcmdata/dcdeftag.h + dcmdata/libsrc/dcdictbi.cc + dcmiod/libsrc/modhelp.cc + dcmqrdb/libsrc/dcmqrdbi.cc + dcmrt/libsrc/drtdose.cc + dcmrt/libsrc/drtimage.cc + dcmrt/libsrc/drtionpl.cc + dcmrt/libsrc/drtiontr.cc + dcmrt/libsrc/drtplan.cc + dcmrt/libsrc/drtstrct.cc + dcmrt/libsrc/drttreat.cc + dcmwlm/libsrc/wlds.cc + +**** Changes from 2025.04.08 (riesmeier) + +- Added note on the potential misuse of options: + Added a security note on the potential misuse of options/parameters + that can lead to an injection of dangerous content into the HTML/XHTML + output. + Thanks to drak for the report and proof of concept. + Affects: dcmsr/docs/dsr2html.man + dcmsr/include/dcmtk/dcmsr/dsrcomvl.h + dcmsr/include/dcmtk/dcmsr/dsrdoc.h + dcmsr/include/dcmtk/dcmsr/dsrdoctn.h + dcmsr/include/dcmtk/dcmsr/dsrdoctr.h + dcmsr/include/dcmtk/dcmsr/dsrimgvl.h + dcmsr/include/dcmtk/dcmsr/dsrwavvl.h + +**** Changes from 2025.04.05 (riesmeier) + +- Updated list of multi-frame Storage SOP Classes: + Updated the list of multi-frame Storage SOP Classes that is used when + generating a DICOMDIR based on an Application Profile that supports + MPEG compression. + Affects: dcmdata/libsrc/dcddirif.cc + +- Added support for new Directory Record Type: + Added support for new Waveform Presentation State Directory Record Type + "WF PRESENTATION" introduced with Supplement 236, i.e. to the DICOMDIR + creating, reading and writing code. + This finally closes DCMTK Conformance #1152. + Affects: dcmdata/include/dcmtk/dcmdata/dcddirif.h + dcmdata/include/dcmtk/dcmdata/dcdirrec.h + dcmdata/libsrc/dcddirif.cc + dcmdata/libsrc/dcdirrec.cc + +**** Changes from 2025.03.24 (eichelberg) + +- Disable Borland workarounds when using bcc64x: + The Embarcadero bcc64x compiler, a Clang-based 64-bit compiler, does not + require or support the workarounds in the DCMTK code implemented once for + Borland 5.5. These workarounds are now disabled if bcc64x is detected. + Thanks to DCMTK forum user "OliWe" for the suggestion. + Affects: CMake/osconfig.h.in + dcmdata/libsrc/dcdict.cc + oflog/include/dcmtk/oflog/config/win32.h + oflog/libsrc/fileap.cc + oflog/libsrc/timehelp.cc + ofstd/libsrc/ofconsol.cc + ofstd/libsrc/ofxml.cc + +**** Changes from 2025.03.22 (riesmeier) + +- Added quotation marks to CMake variables. + Affects: INSTALL + +**** Changes from 2025.03.21 (eichelberg) + +- Improved description of support lib compile flags: + Improved the description of the runtime library selection flags required + when compiling the support libraries on Windows. + Thanks to DCMTK forum user "pierrechatelier" for the suggestion. + Affects: INSTALL + +**** Changes from 2025.03.14 (riesmeier) + +- Added support for new Presentation States: + Added support for new Presentation State SOP Classes introduced with + Supplement 236 (Waveform Presentation State) to the DICOM SR module. + Affects: dcmsr/include/dcmtk/dcmsr/dsrtypes.h + dcmsr/libsrc/dsrtypes.cc + +- Added support for new Storage SOP Classes: + Added definition of new Storage SOP Class UIDs introduced with Supplement + 236 (Waveform Presentation State). Also added support for these Storage + SOP Classes to the various networking tools. + This partly closes DCMTK Conformance #1152. + Affects: dcmdata/include/dcmtk/dcmdata/dcuid.h + dcmdata/libsrc/dcuid.cc + dcmnet/docs/getscu.man + dcmnet/docs/movescu.man + dcmnet/docs/storescp.man + dcmnet/etc/storescp.cfg + dcmnet/etc/storescu.cfg + dcmqrdb/docs/dcmqrscp.man + dcmqrdb/etc/dcmqrprf.cfg + +**** Changes from 2025.03.14 (onken) + +- Don't check for libsndfile in default build: + The public DCMTK does not make any use of libsndfile; therefore there + is no need to check for the library at all. + For CMake, the library is not checked unless DCMTK_WITH_SNDFILE is + enabled by the user. + For autoconf, the presence of libsndfile is only checked if + configured with --with-libsndfile. Only if found the script will + print "checking whether to include libsndfile support...yes" and in + all other cases will (default, --without-libsndfile, --with-libsndfile + but library not found) print "no" instead. + Affects: CMake/dcmtkPrepare.cmake + config/configure + config/configure.in + +**** Changes from 2025.03.08 (eichelberg) + +- Removed unused variable. + Affects: dcmnet/libsrc/dulfsm.cc + +**** Changes from 2025.03.07 (eichelberg) + +- Fixed error message on Windows. + Affects: dcmnet/libsrc/dulfsm.cc + +**** Changes from 2025.03.06 (riesmeier) + +- Added initial support for new transfer syntax: + Added initial support for the Deflated Image Frame Compression Transfer + Syntax that was introduced with Supplement 244. + The current support includes reading and writing of DICOM files and + data sets that are encoded with this new transfer syntax as well as + basic network support (including some of the command line tools). + What is still missing is the compression and decompression of this + transfer syntax, i.e. the lossless transcoding to/from the various + uncompressed transfer syntaxes. + This closes DCMTK Conformance #1149. + Affects: dcmdata/include/dcmtk/dcmdata/dcuid.h + dcmdata/include/dcmtk/dcmdata/dcxfer.h + dcmdata/libsrc/dcuid.cc + dcmdata/libsrc/dcxfer.cc + dcmnet/apps/echoscu.cc + dcmnet/apps/movescu.cc + dcmnet/apps/storescp.cc + dcmnet/docs/echoscu.man + dcmnet/docs/movescu.man + dcmnet/docs/storescp.man + dcmnet/etc/storescp.cfg + dcmnet/etc/storescu.cfg + dcmnet/libsrc/dimse.cc + dcmqrdb/etc/dcmqrprf.cfg + +**** Changes from 2025.03.04 (riesmeier) + +- Use non-throwing new operator: + Use a non-throwing new operator when allocating huge amounts of memory. + Affects: dcmimage/include/dcmtk/dcmimage/dicomot.h + dcmimgle/include/dcmtk/dcmimgle/dimocpt.h + dcmimgle/include/dcmtk/dcmimgle/dimoflt.h + dcmimgle/include/dcmtk/dcmimgle/dimorot.h + dcmimgle/include/dcmtk/dcmimgle/dimosct.h + +- Use non-throwing new operator to avoid crash: + Use a non-throwing new operator to avoid a possible crash when allocating + huge amounts of memory. Before, an uncatched C++ exception could be thrown + when processing an incorrectly encoded DICOM image. + Thanks to drak for the report and sample file. + Affects: dcmimgle/include/dcmtk/dcmimgle/dimoipxt.h + +**** Changes from 2025.03.03 (eichelberg) + +- Fixed ANNOUNCE.369. + Affects: docs/ANNOUNCE.369 + +- Fixed segfault in JPEG-LS decoder: + Fixed a bug in the JPEG-LS decoder that led to a segmentation fault if invalid + input data was processed, due to insufficient validation of input data. + Thanks to Ding zhengzheng for the report + and the sample file (PoC). + This closes DCMTK issue #1155. + Affects: dcmjpls/libcharls/scan.h + +- Fixed bug in RLE decoder: + Fixed bug in RLE decoder that caused an application crash when an RLE + compressed image with a defective RLE header was processed. + Thanks to Simeon Stoykov for the bug report + and test file. + Affects: dcmdata/libsrc/dcrleccd.cc + +**** Changes from 2025.03.02 (riesmeier) + +- Always return unknown PDU type with two digits: + In case of error, return a status message with two digits (hexadecimal + notation) for the unknown PDU type. + Affects: dcmnet/libsrc/dulfsm.cc + +**** Changes from 2025.02.28 (eichelberg) + +- Fixed BMP to DICOM conversion on big-endian CPUs. + Affects: dcmdata/apps/img2dcm.cc + dcmdata/include/dcmtk/dcmdata/libi2d/i2d.h + dcmdata/libi2d/i2d.cc + +- Fixed STL file size validation on big-endian CPUs. + Affects: dcmdata/libsrc/dcencdoc.cc + +**** Changes from 2025.02.27 (riesmeier) + +- Added SOP Class to "DICOM Conformance" section: + Added Rendition Selection Document Real-Time Communication SOP Class + to the list of supported SOP Classes, although it is not a Storage + SOP Class but used for Real-Time Communication (see DICOM PS3.22). + Affects: dcmsr/docs/dsr2html.man + dcmsr/docs/dsr2xml.man + dcmsr/docs/dsrdump.man + dcmsr/docs/xml2dsr.man + +**** Changes from 2025.02.26 (eichelberg) + +- Fixed byte order when decoding JPEG-LS on big endian: + When running on a big endian architecture, the byte swapping algorithm + in the JPEG-LS decoder produced incorrect output when decoding an image + with an odd frame size. + This closes DCMTK issue #1154. + Affects: dcmjpls/include/dcmtk/dcmjpls/djcodecd.h + dcmjpls/libsrc/djcodecd.cc + +- Warn when decoding frame into odd-length buffer: + Print a warning to the logger when decodeFrame() operates on an odd-length + frame size on a big endian machine, because in this case the last pixel + cannot be corrected for byte order. + Affects: dcmdata/libsrc/dcrleccd.cc + dcmjpeg/libsrc/djcodecd.cc + +**** Changes from 2025.02.24 (riesmeier) + +- Updated Context Group classes for DICOM 2025a: + Updated automatically generated Context Group classes for the latest + edition of the DICOM standard. All supported classes were updated, + even though there were no changes to most of them. + Affects: dcmsr/include/dcmtk/dcmsr/cmr/cid100.h + dcmsr/include/dcmtk/dcmsr/cmr/cid10013.h + dcmsr/include/dcmtk/dcmsr/cmr/cid10033.h + dcmsr/include/dcmtk/dcmsr/cmr/cid11.h + dcmsr/include/dcmtk/dcmsr/cmr/cid218.h + dcmsr/include/dcmtk/dcmsr/cmr/cid244.h + dcmsr/include/dcmtk/dcmsr/cmr/cid247.h + dcmsr/include/dcmtk/dcmsr/cmr/cid29.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4020.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4021.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4031.h + dcmsr/include/dcmtk/dcmsr/cmr/cid42.h + dcmsr/include/dcmtk/dcmsr/cmr/cid6147.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7021.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7181.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7445.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7452.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7453.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7464.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7469.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7551.h + dcmsr/libcmr/cid100.cc + dcmsr/libcmr/cid10013.cc + dcmsr/libcmr/cid10033.cc + dcmsr/libcmr/cid11.cc + dcmsr/libcmr/cid218.cc + dcmsr/libcmr/cid244.cc + dcmsr/libcmr/cid247.cc + dcmsr/libcmr/cid29.cc + dcmsr/libcmr/cid4020.cc + dcmsr/libcmr/cid4021.cc + dcmsr/libcmr/cid4031.cc + dcmsr/libcmr/cid42.cc + dcmsr/libcmr/cid6147.cc + dcmsr/libcmr/cid7021.cc + dcmsr/libcmr/cid7181.cc + dcmsr/libcmr/cid7445.cc + dcmsr/libcmr/cid7452.cc + dcmsr/libcmr/cid7453.cc + dcmsr/libcmr/cid7464.cc + dcmsr/libcmr/cid7469.cc + dcmsr/libcmr/cid7551.cc + +- Updated code definitions for DICOM 2025a: + Updated automatically generated code definitions for coding scheme "NCIt". + For the coding schemes "DCM" and "UMLS", there were no changes. + Affects: dcmsr/include/dcmtk/dcmsr/codes/dcm.h + dcmsr/include/dcmtk/dcmsr/codes/ncit.h + dcmsr/include/dcmtk/dcmsr/codes/umls.h + +- Updated data dictionary for DICOM 2025a: + Updated data dictionary for the latest edition of the DICOM standard. + Affects: dcmdata/data/dicom.dic + dcmdata/include/dcmtk/dcmdata/dcdeftag.h + dcmdata/libsrc/dcdictbi.cc + +**** Changes from 2025.02.23 (eichelberg) + +- Fixed Solaris/OpenIndiana build. + Affects: dcmdata/libsrc/dcvrod.cc + dcmdata/libsrc/dcvrof.cc + +**** Changes from 2025.02.21 (riesmeier) + +- Avoid possible output of "-nan" in XML format: + Avoid possible output of "-nan" in XML format in order to be consistent + throughout the toolkit and to avoid problems when reading such data. + This closes DCMTK Bug #1153. + Thanks to Mathieu Malaterre for the report + and sample instructions to reproduce this issue. + Affects: dcmdata/libsrc/dcvrod.cc + dcmdata/libsrc/dcvrof.cc + dcmsr/libsrc/dsrnumvl.cc + +- Made sure float/double values use a decimal point: + Made sure that floating point and double values use a proper decimal + point when output to a C++ stream. This requires to explicitly set the + "C" locale in case the user changed the default, i.e. the global locale. + This partly closes DCMTK Bug #1153. + Thanks to Mathieu Malaterre for the report + on a related issue (that is not yet fixed). + Affects: dcmdata/libsrc/dcvrod.cc + dcmdata/libsrc/dcvrof.cc + dcmimgle/libsrc/dicielut.cc + dcmimgle/libsrc/digsdlut.cc + dcmsr/libsrc/dsrnumvl.cc + +**** Changes from 2025.02.21 (eichelberg) + +- Added support for "-nan" in OFStandard::atof(): + OFStandard::atof() will now accept "-nan" as a valid string + and convert it to a quiet NaN. + Affects: ofstd/libsrc/ofstd.cc + +- Updated some comments. + Affects: ofstd/libsrc/ofstd.cc + +**** Changes from 2025.02.21 (riesmeier) + +- Report warning on incorrect Mapping Resource UID: + Output a warning message when reading the UID that is defined for the + DICOM Content Mapping Resource (DCMR) for a different Mapping Resource. + Also tried to harmonized capitalization of DICOM names in comments. + Affects: dcmsr/libsrc/dsrdoctn.cc + +**** Changes from 2025.02.20 (riesmeier) + +- Report warning on unexpected Verifying Observers: + Output a warning message when reading Verifying Observer(s) from a + document (DICOM or XML) that has a Verification Flag of "UNVERIFIED". + Affects: dcmsr/libsrc/dsrdoc.cc + +**** Changes from 2025.02.20 (eichelberg) + +- Fixed minor inconsistency in macro definition. + Affects: ofstd/libsrc/ofstd.cc + +**** Changes from 2025.02.20 (riesmeier) + +- Increased precision of float/double output to XML: + Increased precision of float output to 9 and double to 17 in order to be + "lossless" when writing/reading to/from XML format. + Affects: dcmdata/libsrc/dcvrof.cc + dcmsr/libsrc/dsrnumvl.cc + +**** Changes from 2025.02.20 (eichelberg) + +- Refuse compilation on platforms without 64-bit int: + Refuse compilation on platforms where no 64-bit integers are available. + This is necessary since the Uint64/Sint64 typedefs are meanwhile used in + various places in the toolkit. + Affects: ofstd/include/dcmtk/ofstd/offile.h + ofstd/include/dcmtk/ofstd/ofrand.h + ofstd/include/dcmtk/ofstd/oftypes.h + ofstd/libsrc/ofrand.cc + +**** Changes from 2025.02.19 (riesmeier) + +- Fixed wrong Doxygen subsection labels. + Affects: dcmdata/docs/dcm2pdf.man + +- Used "@note" in Doxgen's API documentation: + Used "@note" command in Doxygen's API documentation to highlight when + certain methods are only applicable to particular VRs. + Affects: dcmdata/include/dcmtk/dcmdata/dcitem.h + +- Added putAndInsertXXX() methods for 64-bit VRs: + Added helper methods putAndInsertUint64(), putAndInsertUint64Array(), + putAndInsertSint64() and putAndInsertSint64Array() for the still + rather new 64-bit VRs "SV", "UV" and "OV". + Affects: dcmdata/include/dcmtk/dcmdata/dcitem.h + dcmdata/libsrc/dcitem.cc + +- Added missing virtual functions to base class: + Added missing virtual functions putUint64(), putSint64(), putUint64Array() + and putSint64Array() to the DcmElement class. Apparently, they were + forgotten when support for the 64-bit VRs "SV", "UV" and "OV" was added + some years ago. + Thanks to Mathieu Malaterre for the report. + Affects: dcmdata/include/dcmtk/dcmdata/dcelem.h + dcmdata/libsrc/dcelem.cc + +**** Changes from 2025.02.17 (malaterre) + +- Provide a hook to setup iconv path at runtime: + Provide a new function OFiconv_setpath() that allows an application + to change the iconv data path at runtime. Note: the DCMICONVPATH + environment variable will override any path set with this function. + Thanks to Mathieu Malaterre for the pull request. + This closes GitHub PR #116. + Affects: oficonv/include/dcmtk/oficonv/iconv.h + oficonv/libsrc/citrus_bcs.c + oficonv/libsrc/oficonv_iconv.c + +**** Changes from 2025.02.12 (riesmeier) + +- Fixed issue with icon image of IMAGE content item: + Fixed issue with the optional icon image of an IMAGE content item that + was not copied when the setValue() method was called. Depending on how + the underlying DSRImageReferenceValue class was used, this resulted in + the Icon Image Sequence (0088,0200) missing in the created DICOM dataset. + Also added new regression tests that make sure that the icon image is + created correctly and copied when needed. + Thanks to forum user "pintagliata" for the original report. + Added: dcmsr/tests/tsrimgvl.cc + Affects: dcmsr/include/dcmtk/dcmsr/dsrimgvl.h + dcmsr/libsrc/dsrimgvl.cc + dcmsr/tests/CMakeLists.txt + dcmsr/tests/Makefile.dep + dcmsr/tests/Makefile.in + dcmsr/tests/tests.cc + +**** Changes from 2025.02.11 (riesmeier) + +- Added "datadir" and "docdir" to CMake config: + Added CMake variables CMAKE_INSTALL_DATADIR and CMAKE_INSTALL_DOCDIR + to the DCMTK configuration that is exported to "DCMTKConfig.cmake". + This is particularly important as both paths usually contain the DCMTK + version number, which might not be known to the project that imports + the DCMTK configuration. + Also added quotation marks to all exported installation subdirectories + so that spaces and other "special" characters are supported. + Affects: CMake/DCMTKConfig.cmake.in + +**** Changes from 2025.02.11 (eichelberg) + +- Fixed precision when converting double to text: + Fixed a bug in OFStandard::ftoa() that caused floating point numbers + to be converted/printed with less precision than requested. + Thanks to Mathieu Malaterre for the + bug report and test file. + Affects: ofstd/libsrc/ofstd.cc + +**** Changes from 2025.02.11 (riesmeier) + +- Fixed calculation of no. of TLS 1.3 cipher suites: + Fixed wrong calculation of number of TLS 1.3 cipher suites. + Thanks to forum user "Shaeto" for the report. + Affects: dcmtls/libsrc/tlsciphr.cc + +**** Changes from 2025.02.07 (eichelberg) + +- Removed conditional code for very old MSVC versions: + Removed conditionally compiled code for very old MSVC versions + (Visual Studio 2010 and older) that are not supported anymore, + in order to reduce the amount of dead code in the toolkit. + Affects: CMake/osconfig.h.in + dcmdata/tests/tfilter.cc + dcmjpls/libcharls/lltraits.h + oflog/include/dcmtk/oflog/config.h + oflog/include/dcmtk/oflog/config/win32.h + oflog/libsrc/log4judp.cc + oflog/libsrc/threads.cc + oflog/libsrc/timehelp.cc + ofstd/include/dcmtk/ofstd/ofcmdln.h + ofstd/include/dcmtk/ofstd/ofmem.h + ofstd/include/dcmtk/ofstd/ofoption.h + +**** Changes from 2025.02.07 (riesmeier) + +- Replaced spaces by tab character for indendation. + Affects: ofstd/libsrc/Makefile.in + +- Added line break for 80-characters-per-line limit. + Affects: dcmpstat/docs/dcmprscp.man + dcmpstat/docs/dcmprscu.man + +- Added missing newline to "FILES" section. + Affects: dcmsr/docs/dsr2html.man + +**** Changes from 2025.02.07 (eichelberg) + +- Added class that computes SHA-256 checksums: + Added a class in ofstd that computes SHA-256 checksums and does not depend + on OpenSSL. The code is based on the SHA-256 implementation by + Brad Conte (brad AT bradconte.com) in FreeBSD. + Added: ofstd/include/dcmtk/ofstd/ofsha256.h + ofstd/libsrc/ofsha256.cc + Affects: ofstd/libsrc/CMakeLists.txt + ofstd/libsrc/Makefile.in + +**** Changes from 2025.02.06 (riesmeier) + +- Fixed source code formatting. + Affects: dcmdata/libsrc/dcjson.cc + +**** Changes from 2025.02.05 (riesmeier) + +- Fixed minor documentation errors: + Fixed wrong struct name in documentation sample code. Also fixed various + other minor issues in API documentation and comments. + Thanks to GitHub user "malaterre" the original report and proposed patch. + This closes GitHub pull request #115. + Affects: dcmdata/include/dcmtk/dcmdata/dcjson.h + dcmdata/libsrc/dcjson.cc + +- Reverted accidentally committed changes: + Reverted changes that were accidentally committed with commit 9a0189b5a. + Instead, added a comment that there is still a TODO for handling incoming + C-ECHO requests. + Affects: dcmnet/libsrc/dstorscp.cc + +- Removed space between CMake command and "(". + Affects: CMake/3rdparty.cmake + CMake/GenerateDCMTKConfigure.cmake + CMake/dcmtkPrepare.cmake + +**** Changes from 2025.02.04 (riesmeier) + +- Added the usual note for development versions. + Affects: INSTALL + dcmnet/libsrc/dstorscp.cc + +**** Changes from 2025.02.04 (eichelberg) + +- Silently remove trailing zero bytes in strings: + Silently remove trailing zero bytes in character string VRs + if dcmEnableAutomaticInputDataCorrection is enabled. + This closes DCMTK issue #1014. + Affects: dcmdata/libsrc/dcbytstr.cc + +- Updated INSTALL for previous commit. + Affects: INSTALL + +- Stop cmake with error message if C++ standard is 98: + Stop with an error message if somebody tries to compile DCMTK with a compiler + that either does not support C++11, or is configured via CMAKE_CXX_STANDARD + to only use C++98. For now, compilation with C++98 can be enabled be running + cmake -DDCMTK_PERMIT_CXX98=ON, but this will not work anymore in future + DCMTK releases. + Affects: CMake/GenerateDCMTKConfigure.cmake + CMake/dcmtkPrepare.cmake + +**** Changes from 2025.01.30 (eichelberg) + +- Fixed minor warnings. + Affects: dcmdata/libi2d/i2djpgs.cc + dcmdata/libi2d/i2dplnsc.cc + dcmdata/libi2d/i2dplop.cc + dcmdata/libi2d/i2dplvlp.cc + +**** Changes from 2025.01.29 (eichelberg) + +- Improved handling of JPEG image files: + Improved the handling of JPEG image files as input to img2dcm. Several special + cases such as different types of component subsampling, as well as lossy + JPEG images in RGB or CMYK color (instead of YCbCr) are now correctly + handled, and rejected if necessary. + This closes DCMTK issue #995. + Thanks to Mathieu Malaterre for the suggestion. + Affects: dcmdata/include/dcmtk/dcmdata/libi2d/i2d.h + dcmdata/include/dcmtk/dcmdata/libi2d/i2djpgs.h + dcmdata/include/dcmtk/dcmdata/libi2d/i2doutpl.h + dcmdata/include/dcmtk/dcmdata/libi2d/i2dplnsc.h + dcmdata/include/dcmtk/dcmdata/libi2d/i2dplop.h + dcmdata/include/dcmtk/dcmdata/libi2d/i2dplsc.h + dcmdata/include/dcmtk/dcmdata/libi2d/i2dplvlp.h + dcmdata/libi2d/i2d.cc + dcmdata/libi2d/i2djpgs.cc + dcmdata/libi2d/i2dplnsc.cc + dcmdata/libi2d/i2dplop.cc + dcmdata/libi2d/i2dplsc.cc + dcmdata/libi2d/i2dplvlp.cc + +**** Changes from 2025.01.28 (riesmeier) + +- Avoid warning on unused variable: + Avoid warning on unused variable when compiled without character set + conversion support. + Affects: dcmsr/apps/dsr2html.cc + dcmsr/apps/dsrdump.cc + +**** Changes from 2025.01.25 (riesmeier) + +- Added support for the Context Group Keyword: + Added support for the official Context Group Keyword that was introduced + for all Context Groups of the DICOM Content Mapping Resource (DCMR, see + DICOM PS3.16) with CP-2181. + This change required to regenerate all automatically generated Context + Group classes (still based on DICOM 2024e). + This closes DCMTK Feature #1033. + Affects: dcmsr/include/dcmtk/dcmsr/cmr/cid100.h + dcmsr/include/dcmtk/dcmsr/cmr/cid10013.h + dcmsr/include/dcmtk/dcmsr/cmr/cid10033.h + dcmsr/include/dcmtk/dcmsr/cmr/cid11.h + dcmsr/include/dcmtk/dcmsr/cmr/cid218.h + dcmsr/include/dcmtk/dcmsr/cmr/cid244.h + dcmsr/include/dcmtk/dcmsr/cmr/cid247.h + dcmsr/include/dcmtk/dcmsr/cmr/cid29.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4020.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4021.h + dcmsr/include/dcmtk/dcmsr/cmr/cid4031.h + dcmsr/include/dcmtk/dcmsr/cmr/cid42.h + dcmsr/include/dcmtk/dcmsr/cmr/cid6147.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7021.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7181.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7445.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7452.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7453.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7464.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7469.h + dcmsr/include/dcmtk/dcmsr/cmr/cid7551.h + dcmsr/include/dcmtk/dcmsr/dsrctxgr.h + dcmsr/libcmr/cid100.cc + dcmsr/libcmr/cid10013.cc + dcmsr/libcmr/cid10033.cc + dcmsr/libcmr/cid11.cc + dcmsr/libcmr/cid218.cc + dcmsr/libcmr/cid244.cc + dcmsr/libcmr/cid247.cc + dcmsr/libcmr/cid29.cc + dcmsr/libcmr/cid4020.cc + dcmsr/libcmr/cid4021.cc + dcmsr/libcmr/cid4031.cc + dcmsr/libcmr/cid42.cc + dcmsr/libcmr/cid5000.cc + dcmsr/libcmr/cid5001.cc + dcmsr/libcmr/cid6147.cc + dcmsr/libcmr/cid7021.cc + dcmsr/libcmr/cid7181.cc + dcmsr/libcmr/cid7445.cc + dcmsr/libcmr/cid7452.cc + dcmsr/libcmr/cid7453.cc + dcmsr/libcmr/cid7464.cc + dcmsr/libcmr/cid7469.cc + dcmsr/libcmr/cid7551.cc + dcmsr/libsrc/dsrctxgr.cc + dcmsr/tests/tsrcmr.cc + +**** Changes from 2025.01.24 (eichelberg) + +- Improved diagnostic log output. + Affects: dcmdata/libsrc/dcrleccd.cc + +**** Changes from 2025.01.23 (eichelberg) + +- Fixed issue with invalid RLE compressed DICOM images: + Fixed issue when processing an RLE compressed image where the RLE header + contains an invalid stripe size. + Thanks to Ding zhengzheng for the report + and the sample file (PoC). + Affects: dcmdata/libsrc/dcrleccd.cc + +**** Changes from 2025.01.22 (eichelberg) + +- Fixed access rights. + Affects: docs/ANNOUNCE.369 + +**** Changes from 2025.01.21 (riesmeier) + +- Made sure all member variables are initialized: + Initialized member variable that was missing in the member initializer + list of the constructor. This issue was reported at runtime when compiling + with gcc and option -fsanitize=undefined. + Affects: dcmiod/libsrc/modmultiframedimension.cc + +- Fixed another issue with invalid DICOM images: + Fixed issue when processing an invalid DICOM image where the number of + pixels stored does not match the expected number of pixels (too less) + and the combination of BitsAllocated and BitsStored is really unusual + (e.g. 1 bit stored, but 52 bits allocated). In cases where the last + pixel (e.g. a single bit) does not fit into the buffer of the input + pixel data, a buffer overflow occurred on the heap. Now, the last entry + of the buffer is filled with the smallest possible value (e.g. 0 in case + of unsigned data). + Thanks to Ding zhengzheng for the report + and the sample file (PoC). + Affects: dcmimgle/include/dcmtk/dcmimgle/diinpxt.h + +- Don't call getAbsMinimum() in a for-loop: + Use a local constant instead of calling getAbsMinimum() in a for-loop, + even if it is probably expanded as an inline function. + Affects: dcmimgle/include/dcmtk/dcmimgle/diinpxt.h + +**** Changes from 2025.01.20 (riesmeier) + +- Removed further outdated Autoconf tests: + See commits 09017a3cb and 95c799f57 for details and the corresponding + CMake changes. + Affects: config/aclocal.m4 + config/configure + config/configure.in + config/confmod + config/include/dcmtk/config/osconfig.h.in + +**** Changes from 2025.01.20 (eichelberg) + +- Further clean-up of outdated configure tests. + Affects: CMake/GenerateDCMTKConfigure.cmake + CMake/osconfig.h.in + +**** Changes from 2025.01.19 (eichelberg) + +- Fixed character set conversion in dcm2json: + Fixed character set conversion in dcm2json for the case that an extended + character set is used but that character set that does not contain byte + positions > 127 (such as ISO_IR 87) and is thus not detected by + DcmItem::containsExtendedCharacters(). + Affects: dcmdata/apps/dcm2json.cc + +- Fixed debug build on Win32. + Affects: dcmdata/libsrc/dcistrmz.cc + +**** Changes from 2025.01.18 (eichelberg) + +- Further clean-up of outdated configure tests: + Cleaned up configure tests that checked for features that are not used + in DCMTK anymore or that are not in use anymore on any of the supported + operating systems (e.g. pre-POSIX APIs and header files). + Affects: CMake/GenerateDCMTKConfigure.cmake + CMake/osconfig.h.in + config/docs/macros.txt + config/math.cc + dcmapps/include/dcmtk/dcmapps/dcm2img.h + dcmdata/apps/dcm2pdf.cc + dcmdata/apps/dump2dcm.cc + dcmdata/include/dcmtk/dcmdata/dctypes.h + dcmdata/libdcxml/xml2dcm.cc + dcmdata/libsrc/cmdlnarg.cc + dcmdata/libsrc/dcbytstr.cc + dcmdata/libsrc/dcelem.cc + dcmdata/libsrc/dcistrmf.cc + dcmdata/libsrc/dcistrms.cc + dcmdata/libsrc/dcostrmf.cc + dcmdata/libsrc/dcostrms.cc + dcmdata/libsrc/dcuid.cc + dcmimage/include/dcmtk/dcmimage/dicopxt.h + dcmimage/include/dcmtk/dcmimage/diqtpbox.h + dcmimgle/include/dcmtk/dcmimgle/diinpxt.h + dcmimgle/include/dcmtk/dcmimgle/dimopxt.h + dcmjpeg/libijg12/jconfig12.h + dcmjpeg/libijg12/jerror.c + dcmjpeg/libijg16/jconfig16.h + dcmjpeg/libijg16/jerror.c + dcmjpeg/libijg8/jconfig8.h + dcmjpeg/libijg8/jerror.c + dcmjpls/libcharls/encodstr.h + dcmjpls/libcharls/streams.h + dcmjpls/libsrc/djcodece.cc + dcmnet/apps/storescp.cc + dcmnet/include/dcmtk/dcmnet/dcompat.h + dcmnet/libsrc/assoc.cc + dcmnet/libsrc/dcmtrans.cc + dcmnet/libsrc/dcompat.cc + dcmnet/libsrc/dimcancl.cc + dcmnet/libsrc/dimcmd.cc + dcmnet/libsrc/dimdump.cc + dcmnet/libsrc/dimfind.cc + dcmnet/libsrc/dimget.cc + dcmnet/libsrc/dimmove.cc + dcmnet/libsrc/dimse.cc + dcmnet/libsrc/dimstore.cc + dcmnet/libsrc/diutil.cc + dcmnet/libsrc/dul.cc + dcmnet/libsrc/dulextra.cc + dcmnet/libsrc/dulfsm.cc + dcmpstat/apps/dcmprscp.cc + dcmpstat/apps/dcmprscu.cc + dcmpstat/apps/dcmpsrcv.cc + dcmpstat/apps/dcmpssnd.cc + dcmpstat/libsrc/dviface.cc + dcmpstat/libsrc/dvpshlp.cc + dcmpstat/tests/msgserv.cc + dcmqrdb/apps/dcmqrscp.cc + dcmqrdb/libsrc/dcmqrcbg.cc + dcmqrdb/libsrc/dcmqrcbm.cc + dcmqrdb/libsrc/dcmqrcnf.cc + dcmqrdb/libsrc/dcmqrdbi.cc + dcmqrdb/libsrc/dcmqrtis.cc + dcmsr/libsrc/dsrxmld.cc + dcmtls/libsrc/tlstrans.cc + dcmwlm/libsrc/wldsfs.cc + oficonv/apps/mkcsmapper.y + oficonv/apps/mkcsmapper_bison.cc + oficonv/apps/mkcsmapper_bison.h + oficonv/apps/mkesdb.y + oficonv/apps/mkesdb_bison.cc + oficonv/apps/mkesdb_bison.h + oficonv/include/dcmtk/oficonv/iconv.h + oficonv/libsrc/citrus_bcs.h + oficonv/libsrc/citrus_big5.c + oficonv/libsrc/citrus_csmapper.c + oficonv/libsrc/citrus_db.c + oficonv/libsrc/citrus_db_factory.c + oficonv/libsrc/citrus_db_hash.c + oficonv/libsrc/citrus_dechanyu.c + oficonv/libsrc/citrus_esdb.c + oficonv/libsrc/citrus_euc.c + oficonv/libsrc/citrus_euctw.c + oficonv/libsrc/citrus_gbk2k.c + oficonv/libsrc/citrus_hash.c + oficonv/libsrc/citrus_hz.c + oficonv/libsrc/citrus_iconv.c + oficonv/libsrc/citrus_iso2022.c + oficonv/libsrc/citrus_jisx0208.c + oficonv/libsrc/citrus_johab.c + oficonv/libsrc/citrus_lookup.c + oficonv/libsrc/citrus_mapper.c + oficonv/libsrc/citrus_mmap.c + oficonv/libsrc/citrus_module.c + oficonv/libsrc/citrus_mskanji.c + oficonv/libsrc/citrus_none.c + oficonv/libsrc/citrus_region.h + oficonv/libsrc/citrus_types.h + oficonv/libsrc/citrus_utf1632.c + oficonv/libsrc/citrus_utf8.c + oficonv/libsrc/citrus_viqr.c + oficonv/libsrc/citrus_zw.c + oficonv/libsrc/oficonv_iconv.c + oflog/include/dcmtk/oflog/config/defines.h + ofstd/include/dcmtk/ofstd/ofdate.h + ofstd/include/dcmtk/ofstd/offile.h + ofstd/include/dcmtk/ofstd/oflist.h + ofstd/include/dcmtk/ofstd/ofsockad.h + ofstd/include/dcmtk/ofstd/ofstd.h + ofstd/include/dcmtk/ofstd/ofstdinc.h + ofstd/include/dcmtk/ofstd/ofstream.h + ofstd/include/dcmtk/ofstd/oftempf.h + ofstd/include/dcmtk/ofstd/oftime.h + ofstd/include/dcmtk/ofstd/oftypes.h + ofstd/include/dcmtk/ofstd/ofvector.h + ofstd/libsrc/ofconsol.cc + ofstd/libsrc/ofdatime.cc + ofstd/libsrc/offilsys.cc + ofstd/libsrc/offname.cc + ofstd/libsrc/ofipc.cc + ofstd/libsrc/ofstd.cc + +**** Changes from 2025.01.17 (riesmeier) + +- Fixed another issue with invalid mono images: + Fixed issue when rendering an invalid monochrome DICOM image where the + number of pixels stored does not match the expected number of pixels. + In this case, only a single pixel is processed, but the pixel matrix is + much larger. Filling the rest of the pixel matrix with the smallest + possible value for the image is not working because of an optimized + memory usage (value would be out of range). Now, the pixel value to be + used is double-checked before it is actually filled into the "background" + of the image. + Thanks to Ding zhengzheng for the report + and the sample file (PoC). + Affects: dcmimgle/include/dcmtk/dcmimgle/dimoipxt.h + +- Made sure that memory is always cleaned up: + Made sure that allocated memory is always cleaned up, i.e. also in case + of error. Otherwise, tools like ASAN will report memory leaks. + Affects: dcmapps/include/dcmtk/dcmapps/dcm2img.h + +- Do not exceed the maximum size of DcmList: + Made sure that the maximum number of entries of an DcmList instance is + not exceeded. Even if it is unlikely, NULL is returned and a message is + output to the debug logger. + Affects: dcmdata/include/dcmtk/dcmdata/dclist.h + dcmdata/libsrc/dclist.cc + +- Further optimizations of DcmList:seek_to(): + Handle the special cases seek_to() first and last item in the list more + efficiently. Also avoid unnecessary call of DcmList::get() in case of an + invalid position. + Affects: dcmdata/libsrc/dclist.cc + +- Replaced call of DcmList::seek_to() by seek(): + Replaced call of DcmList::seek_to() by seek() for accessing the first + and the last item of the list. This should slightly improve the + performance when DcmFileFormat::getMetaInfo() or getDataset() are called + many times. + Affects: dcmdata/libsrc/dcfilefo.cc + +- Improved performance of DcmList::seek_to(): + Significantly improved the performance of the seek_to() method, in + particular when iterating a very long list using the position of the + items, e.g. a DcmPixelSequence with a huge number of instances of + DcmPixelItem such as in Whole Slide Imaging (WSI) objects. Internally, + the seek_to() method is, e.g., used in the insert(), remove() and + getItem() methods of DcmSequenceOfItems and DcmPixelSequence as well + as in the getElement() and remove() method of DcmItem. + Thanks to GitHub user "reunanen" for the report and proposed patch. + This closes GitHub pull request #114. + Affects: dcmdata/include/dcmtk/dcmdata/dclist.h + dcmdata/libsrc/dclist.cc + dcmdata/tests/tsequen.cc + +**** Changes from 2025.01.17 (eichelberg) + +- Fixed handling of SpecificCharacterSet in sequences: + Fixed the handling of files where SpecificCharacterSet may occur + in sequence items and where different items might use different + character sets, such as DICOMDIRs. + Thanks to Mathieu Malaterre + for pointing out the issue. + Affects: dcmdata/apps/dcm2json.cc + +**** Changes from 2025.01.15 (riesmeier) + +- Made a minor change to the API documentation. + Affects: dcmsr/include/dcmtk/dcmsr/dsrdoc.h + +**** Changes from 2025.01.14 (riesmeier) + +- Restructured code of method convertString(): + Restructured code of the convertString() method by splitting it into two + methods: one for strings without and one for strings with code extension + techniques according to ISO 2022. This should increase the readability + of the code. + Affects: dcmdata/include/dcmtk/dcmdata/dcspchrs.h + dcmdata/libsrc/dcspchrs.cc + +**** Changes from 2025.01.13 (eichelberg) + +- Fixed HAVE_PROTOTYPE_FEENABLEEXCEPT configure test. + Affects: CMake/GenerateDCMTKConfigure.cmake + +- Fixed a few memory leaks in unit tests. + Affects: dcmdata/tests/tfrmsiz.cc + dcmect/tests/t_overflow.cc + dcmfg/tests/t_fg_base.cc + dcmseg/tests/tutils.cc + +- Fixed two memory leaks in oficonv. + Affects: oficonv/include/dcmtk/oficonv/iconv.h + oficonv/libsrc/citrus_iconv.c + oficonv/libsrc/citrus_iconv.h + oficonv/libsrc/citrus_mapper_zone.c + oficonv/libsrc/oficonv_iconv.c + oficonv/tests/tests.cc + oficonv/tests/ticonv.cc + +**** Changes from 2025.01.13 (riesmeier) + +- Enhanced dcm2xml's support for DICOMDIR files: + Fixed conversion of DICOMDIR files to UTF-8 encoding (i.e. when using + option +U8) by removing an irritating error message and making sure + that no SpecificCharacterSet element is added to the top-level dataset. + Affects: dcmdata/apps/dcm2xml.cc + +**** Changes from 2025.01.11 (riesmeier) + +- Fixed issue rendering invalid monochrome image: + Fixed issue when rendering an invalid monochrome DICOM image where the + number of pixels stored does not match the expected number of pixels. + If the stored number is less than the expected number, the rest of the + pixel matrix for the intermediate representation was always filled with + the value 0. Under certain, very rare conditions, this could result in + memory problems reported by an Address Sanitizer (ASAN). Now, the rest + of the matrix is filled with the smallest possible value for the image. + Thanks to Emmanuel Tacheau from the Cisco Talos team + for the original report, the sample + file (PoC) and further details. See TALOS-2024-2122 and CVE-2024-47796. + Affects: dcmimgle/include/dcmtk/dcmimgle/dimoipxt.h + +- Enhanced check for invalid NULL parameter: + Even if the passed value should never be NULL, such a check does not + do any harm. + Affects: dcmimgle/include/dcmtk/dcmimgle/dimoipxt.h + +- Replaced call of delete by delete[]: + This issue has been reported by the gcc address sanitizer (using option + -fsanitize=address). + Affects: dcmimgle/libsrc/diimage.cc + +**** Changes from 2025.01.10 (riesmeier) + +- Fixed issue with Doxygen grouping markup: + Fixed issue with grouping markup when using newer Doxygen versions. + Affects: dcmdata/include/dcmtk/dcmdata/dcbytstr.h + dcmdata/include/dcmtk/dcmdata/dcerror.h + dcmdata/include/dcmtk/dcmdata/dctypes.h + dcmimgle/include/dcmtk/dcmimgle/diutils.h + dcmrt/include/dcmtk/dcmrt/drttypes.h + dcmseg/include/dcmtk/dcmseg/segtypes.h + dcmsr/include/dcmtk/dcmsr/cmr/tid1500.h + dcmsr/include/dcmtk/dcmsr/cmr/tid15def.h + dcmsr/include/dcmtk/dcmsr/cmr/tid1600.h + dcmsr/include/dcmtk/dcmsr/dsrtypes.h + ofstd/include/dcmtk/ofstd/ofchrenc.h + ofstd/include/dcmtk/ofstd/ofcond.h + ofstd/include/dcmtk/ofstd/ofstd.h + ofstd/include/dcmtk/ofstd/oftest.h + +**** Changes from 2025.01.06 (eichelberg) + +- Fixed minor warning on MSVC. + Affects: ofstd/tests/tstring.cc + +**** Changes from 2025.01.05 (riesmeier) + +- Fixed wrong capitalization of acronym "JSON". + Affects: dcmdata/libsrc/dcjson.cc + dcmdata/libsrc/dcvrds.cc + dcmdata/libsrc/dcvris.cc + +**** Changes from 2025.01.05 (eichelberg) + +- Added OFString/std::string conversion helpers: + Added helper functions to convert between std::string and OFString. + This closes DCMTK feature #1106. + Added: ofstd/include/dcmtk/ofstd/ofstrhlp.h + Affects: ofstd/tests/tests.cc + ofstd/tests/tstring.cc + +**** Changes from 2025.01.04 (eichelberg) + +- Added logger warnings when converting invalid IS/DS: + Added logger warnings when converting invalid IS/DS values to Json. + This closes DCMTK Feature #1084. + Affects: dcmdata/libsrc/dcvrds.cc + dcmdata/libsrc/dcvris.cc + +**** Changes from 2025.01.03 (riesmeier) + +- Revised handling of --write-auto option: + Revised handling of --write-auto option to avoid unwanted log messages. + Also slightly modified log messages for reasons of consistency. + Affects: dcmapps/include/dcmtk/dcmapps/dcm2img.h + +- Added check to make sure: HighBit < BitsAllocated: + Added check to the image preprocessing to make sure that the value of + HighBit is always less than the value of BitsAllocated. Before, this + missing check could lead to memory corruption if an invalid combination + of values was retrieved from a malformed DICOM dataset. + Thanks to Emmanuel Tacheau from the Cisco Talos team + for the report, sample file (PoC) + and detailed analysis. See TALOS-2024-2121 and CVE-2024-52333. + Affects: dcmimgle/libsrc/diimage.cc + +- Avoided warning on unchecked option --write-auto. + Affects: dcmapps/include/dcmtk/dcmapps/dcm2img.h + +**** Changes from 2024.12.31 (eichelberg) + +- Fixed data corruption in iconv passthrough mode: + Fixed a data corruption that occured if a string longer than 1024 characters + was run through OFiconv() with source and character set being identical + and the pass-through mode being enabled (i.e. DCMTK compiled with the macro + DCMTK_ENABLE_ICONV_PASSTHROUGH). In this situation, the pointer to the next + unprocessed input character was not updated. + Thanks to DCMTK forum user cschreib for the bug report and test program. + This closes DCMTK issue #1143. + Affects: oficonv/libsrc/citrus_iconv_none.c + +**** Changes from 2024.12.29 (eichelberg) + +- Fixed thread issues in oficonv code: + Fixed various thread issues in the oficonv module reported by Valgrind's + DRD tool by moving some attributes that are modified during a call to + OFiconv_open() from the structure that is shared between multiple + iconv handles (struct _citrus_iconv_shared) to the structure containing + the unique attributes of each handle (struct _citrus_iconv). + Also added a read-write lock protecting accesses to the oficonv + logger callback. + Thanks to Mathieu Malaterre for the bug report + and test program demonstrating the issue. + This closes DCMTK issue #1137. + Affects: oficonv/libsrc/citrus_iconv_local.h + oficonv/libsrc/citrus_iconv_std.c + oficonv/libsrc/citrus_lock.h + oficonv/libsrc/oficonv_iconv.c + oficonv/libsrc/oficonv_logger.c + +**** Changes from 2024.12.28 (eichelberg) + +- Fixed frame size calculation for special cases: + DcmElement::getUncompressedFrameSize() now takes into account special cases + such as compressed images where Bits Allocated has a value that is not a + multiple of 8, but is overwritten during decompression anyway. In the case + of compressed images, the active decoder is now looked up and its behavior is + taken into account in the calculation. + This closes DCMTK issue #1140. + Added: dcmdata/tests/tfrmsiz.cc + Affects: dcmdata/include/dcmtk/dcmdata/dccodec.h + dcmdata/include/dcmtk/dcmdata/dcelem.h + dcmdata/include/dcmtk/dcmdata/dcpixel.h + dcmdata/include/dcmtk/dcmdata/dcrleccd.h + dcmdata/include/dcmtk/dcmdata/dcrlecce.h + dcmdata/libsrc/dccodec.cc + dcmdata/libsrc/dcelem.cc + dcmdata/libsrc/dcpixel.cc + dcmdata/libsrc/dcrleccd.cc + dcmdata/libsrc/dcrlecce.cc + dcmdata/tests/CMakeLists.txt + dcmdata/tests/Makefile.in + dcmdata/tests/tests.cc + dcmjpeg/include/dcmtk/dcmjpeg/djcodecd.h + dcmjpeg/include/dcmtk/dcmjpeg/djcodece.h + dcmjpeg/libsrc/djcodecd.cc + dcmjpeg/libsrc/djcodece.cc + dcmjpls/include/dcmtk/dcmjpls/djcodecd.h + dcmjpls/include/dcmtk/dcmjpls/djcodece.h + dcmjpls/libsrc/djcodecd.cc + dcmjpls/libsrc/djcodece.cc + +**** Changes from 2024.12.27 (riesmeier) + +- Fixed source code formatting. + Affects: dcmimage/libsrc/dipitiff.cc + +**** Changes from 2024.12.24 (eichelberg) + +- Fixed TIFF export on Windows: + Fixed the TIFF export option of the dcm2img tool, which did not work + on 32-bit and 64 bit Windows builds. + Affects: dcmimage/libsrc/dipitiff.cc + +**** Changes from 2024.12.20 (eichelberg) + +- Added option that allows to relax GSPS validation: + Added an option to the validation code for Grayscale Softcopy Presentation + States that allows to relax the rule that all images referenced from one + presentation state must belong to the same SOP class. The rule can either + be relaxed to permit related "for presentation" and "for processing" + SOP classes, or this specific check can be completely disabled. + Thanks to Oliver Klerx for the proposal and patch. + Affects: dcmpstat/apps/dcmpschk.cc + dcmpstat/apps/dcmpsmk.cc + dcmpstat/apps/dcmpsrcv.cc + dcmpstat/docs/dcmpschk.man + dcmpstat/docs/dcmpsmk.man + dcmpstat/etc/dcmpstat.cfg + dcmpstat/include/dcmtk/dcmpstat/dvpscf.h + dcmpstat/include/dcmtk/dcmpstat/dvpsri.h + dcmpstat/libsrc/dvpscf.cc + dcmpstat/libsrc/dvpsri.cc + +**** Changes from 2024.12.18 (eichelberg) + +- Properly handle unknown "localhost" in DVPSIPCClient: + The request to resolve the hostname "localhost" to an IPv4 address + may fail under certain circumstances (e.g. in a IPv6 only environment). + This situation is now handled properly. + Thanks to Oliver Klerx for the bug report and patch. + Affects: dcmpstat/libsrc/dvpsmsg.cc + +- Simplified DcmQuantColorTable::computeHistogram(): + Simplified code to avoid bogus error reports from Cppcheck. + Thanks to Oliver Klerx for the suggestion and patch. + Affects: dcmimage/libsrc/diqtctab.cc + +- Removed duplicate library dependencies (part 2): + Removed link libraries that are automatically included by CMake as + dependencies from other libraries. This avoids the code to be linked + against the same library multiple times, which causes warnings on MacOS. + Affects: dcmdata/apps/CMakeLists.txt + dcmdata/tests/CMakeLists.txt + dcmect/tests/CMakeLists.txt + dcmfg/tests/CMakeLists.txt + dcmjpls/apps/CMakeLists.txt + dcmnet/apps/CMakeLists.txt + dcmpstat/apps/CMakeLists.txt + dcmpstat/tests/CMakeLists.txt + dcmrt/apps/CMakeLists.txt + dcmrt/tests/CMakeLists.txt + dcmtls/tests/CMakeLists.txt + dcmwlm/apps/CMakeLists.txt + dcmwlm/tests/CMakeLists.txt + +**** Changes from 2024.12.17 (eichelberg) + +- Fixed padding of compressed JPEG-LS bitstream: + When using the cooked JPEG-LS encoder, odd-length bitstreams were padded with + a trailing zero byte (corresponding to the --padding-zero command line option) + instead of a an extended EOI marker. + Thanks to Mathieu Malaterre for the bug report and pull request. + This closes Github pull request #111. + Affects: dcmjpls/libsrc/djcodece.cc + +- Removed duplicate library dependencies: + Removed link libraries that are automatically included by CMake as + dependencies from other libraries. This avoids the code to be linked + against the same library multiple times, which causes warnings on MacOS. + Affects: dcmapps/apps/CMakeLists.txt + +**** Changes from 2024.12.17 (fedorov) + +- Fix environ for Apple: + On Apple systems, define environ by using _NSGetEnviron() from + crt_externs.h. + Affects: ofstd/libsrc/ofstub.cc + +**** Changes from 2024.12.16 (riesmeier) + +- Replaced call of delete also in destructor. + Affects: dcmimgle/libsrc/dimoimg.cc + +- Replaced call of delete by delete[]: + Memory that was allocated with new[] should be deleted with delete[]. + Thanks to Oliver Klerx for the report. + Affects: dcmimgle/libsrc/dimoimg.cc + +**** Changes from 2024.12.13 (eichelberg) + +- Updated version information for 3.6.9+ development. + Affects: config/configure + config/configure.in + config/confmod + +- Updated version information for 3.6.9+ development: + Updated version information marking the start of DCMTK development post minor + release 3.6.9. + Moved official ANNOUNCE file of the DCMTK release 3.6.9 to the "docs" + subfolder and replaced the main ANNOUNCE file with a "dummy". + Added: docs/ANNOUNCE.369 + Affects: ANNOUNCE + CMake/dcmtkPrepare.cmake + VERSION + +- Updated Github workflow config. + Affects: .github/workflows/cmake-win.yml + +**** Changes from 2024.12.11 (riesmeier) + +- Added missing package date. + Affects: config/configure + config/configure.in + +**** Changes from 2024.12.11 (eichelberg) + +- Updated PACKAGE version settings. + Affects: config/configure + config/configure.in + +- Updated configure files for upcoming release. + Affects: config/configure + config/configure.in diff --git a/doxygen/manpages/man1/cda2dcm.1 b/doxygen/manpages/man1/cda2dcm.1 index a44c6215..372e93c2 100644 --- a/doxygen/manpages/man1/cda2dcm.1 +++ b/doxygen/manpages/man1/cda2dcm.1 @@ -1,4 +1,4 @@ -.TH "cda2dcm" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "cda2dcm" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME cda2dcm \- Encapsulate CDA file into DICOM file format @@ -12,221 +12,10 @@ cda2dcm [options] cdafile-in dcmfile-out .PP .SH "DESCRIPTION" .PP -The \fBcda2dcm\fP utility reads a CDA file (\fIcdafile-in\fP), converts it to a DICOM Encapsulated CDA Storage SOP instance and stores the converted data to an output file (\fIdcmfile-out\fP)\&. -.SH "PARAMETERS" +The \fBcda2dcm\fP tool is deprecated\&. Use \fBdcmencap\fP instead, which supports the same command line parameters, and more\&. +.SH "SEE ALSO" .PP -.PP -.nf -cdafile-in CDA input filename to be encapsulated - -dcmfile-out DICOM output filename ("-" for stdout) -.fi -.PP -.SH "OPTIONS" -.PP -.SS "general options" -.PP -.nf - -h --help - print this help text and exit - - --version - print version information and exit - - --arguments - print expanded command line arguments - - -q --quiet - quiet mode, print no warnings and errors - - -v --verbose - verbose mode, print processing details - - -d --debug - debug mode, print debug information - - -ll --log-level [l]evel: string constant - (fatal, error, warn, info, debug, trace) - use level l for the logger - - -lc --log-config [f]ilename: string - use config file f for the logger -.fi -.PP -.SS "DICOM document options" -.PP -.nf -document title: - - +t --title [t]itle: string (default: empty) - document title - - +cn --concept-name [CSD] [CV] [CM]: string (default: empty) - coded representation of document title defined by coding - scheme designator CSD, code value CV and code meaning CM - -patient data: - - +pn --patient-name [n]ame: string - patient's name in DICOM PN syntax - - +pi --patient-id [i]d: string - patient identifier - - +pb --patient-birthdate [d]ate: string (YYYYMMDD) - patient's birth date - - +ps --patient-sex [s]ex: string (M, F or O) - patient's sex - -study and series: - - +sg --generate - generate new study and series UIDs (default) - - +st --study-from [f]ilename: string - read patient/study data from DICOM file - - +se --series-from [f]ilename: string - read patient/study/series data from DICOM file - -instance number: - - +i1 --instance-one - use instance number 1 (default, not with +se) - - +ii --instance-inc - increment instance number (only with +se) - - +is --instance-set [i]nstance number: integer - use instance number i - -burned-in annotation: - - +an --annotation-yes - document contains patient identifying data (default) - - -an --annotation-no - document does not contain patient identifying data - -override CDA file data: - - -ov --no-override - CDA patient and document data must match study, - series or manually entered information (default) - - +ov --override - data obtained from the CDA file will be overwritten - by study, series, or manually entered information -.fi -.PP -.SS "processing options" -.PP -.nf -other processing options: - - -k --key [k]ey: gggg,eeee="str", path or dictionary name="str" - add further attribute -.fi -.PP -.SS "output options" -.PP -.nf -output file format: - - +F --write-file - write file format (default) - - -F --write-dataset - write data set without file meta information - -group length encoding: - - +g= --group-length-recalc - recalculate group lengths if present (default) - - +g --group-length-create - always write with group length elements - - -g --group-length-remove - always write without group length elements - -length encoding in sequences and items: - - +e --length-explicit - write with explicit lengths (default) - - -e --length-undefined - write with undefined lengths - -data set trailing padding (not with --write-dataset): - - -p --padding-off - no padding (implicit if --write-dataset) - - +p --padding-create [f]ile-pad [i]tem-pad: integer - align file on multiple of f bytes - and items on multiple of i bytes -.fi -.PP -.SH "NOTES" -.PP -.SS "Attribute Sources" -The application may be fed with some additional input for filling mandatory (and optional) attributes in the new DICOM file like patient, study and series information: -.PP -.IP "\(bu" 2 -The \fI--key\fP option can be used to add further attributes to the DICOM output file\&. -.IP "\(bu" 2 -It is also possible to specify sequences, items and nested attributes using the \fI--key\fP option\&. In these cases, a special 'path' notation has to be used\&. Details on this path notation can be found in the documentation of \fBdcmodify\fP\&. -.IP "\(bu" 2 -The \fI--key\fP option can be present more than once\&. -.IP "\(bu" 2 -The value part (after the '=') may be absent causing the attribute to be set with zero length\&. -.IP "\(bu" 2 -Please be advised that the \fI--key\fP option is applied at the very end, just before saving the DICOM file, so there is no value checking whatsoever\&. -.PP -.SH "LOGGING" -.PP -The level of logging output of the various command line tools and underlying libraries can be specified by the user\&. By default, only errors and warnings are written to the standard error stream\&. Using option \fI--verbose\fP also informational messages like processing details are reported\&. Option \fI--debug\fP can be used to get more details on the internal activity, e\&.g\&. for debugging purposes\&. Other logging levels can be selected using option \fI--log-level\fP\&. In \fI--quiet\fP mode only fatal errors are reported\&. In such very severe error events, the application will usually terminate\&. For more details on the different logging levels, see documentation of module 'oflog'\&. -.PP -In case the logging output should be written to file (optionally with logfile rotation), to syslog (Unix) or the event log (Windows) option \fI--log-config\fP can be used\&. This configuration file also allows for directing only certain messages to a particular output stream and for filtering certain messages based on the module or application where they are generated\&. An example configuration file is provided in \fI/logger\&.cfg\fP\&. -.SH "COMMAND LINE" -.PP -All command line tools use the following notation for parameters: square brackets enclose optional values (0-1), three trailing dots indicate that multiple values are allowed (1-n), a combination of both means 0 to n values\&. -.PP -Command line options are distinguished from parameters by a leading '+' or '-' sign, respectively\&. Usually, order and position of command line options are arbitrary (i\&.e\&. they can appear anywhere)\&. However, if options are mutually exclusive the rightmost appearance is used\&. This behavior conforms to the standard evaluation rules of common Unix shells\&. -.PP -In addition, one or more command files can be specified using an '@' sign as a prefix to the filename (e\&.g\&. \fI@command\&.txt\fP)\&. Such a command argument is replaced by the content of the corresponding text file (multiple whitespaces are treated as a single separator unless they appear between two quotation marks) prior to any further evaluation\&. Please note that a command file cannot contain another command file\&. This simple but effective approach allows one to summarize common combinations of options/parameters and avoids longish and confusing command lines (an example is provided in file \fI/dumppat\&.txt\fP)\&. -.SH "EXIT CODES" -.PP -The \fBcda2dcm\fP utility uses the following exit codes when terminating\&. This enables the user to check for the reason why the application terminated\&. -.SS "general" -.PP -.nf -EXITCODE_NO_ERROR 0 -EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 -EXITCODE_MEMORY_EXHAUSTED 4 -.fi -.PP -.SS "input file errors" -.PP -.nf -EXITCODE_CANNOT_READ_INPUT_FILE 20 -EXITCODE_NO_INPUT_FILES 21 -EXITCODE_INVALID_INPUT_FILE 22 -.fi -.PP -.SS "output file errors" -.PP -.nf -EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 -.fi -.PP -.SH "ENVIRONMENT" -.PP -The \fBcda2dcm\fP utility will attempt to load DICOM data dictionaries specified in the \fIDCMDICTPATH\fP environment variable\&. By default, i\&.e\&. if the \fIDCMDICTPATH\fP environment variable is not set, the file \fI/dicom\&.dic\fP will be loaded unless the dictionary is built into the application (default for Windows)\&. -.PP -The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. +\fBdcmencap\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2018-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2018-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcm2cda.1 b/doxygen/manpages/man1/dcm2cda.1 index 8d62b904..12d6e0c1 100644 --- a/doxygen/manpages/man1/dcm2cda.1 +++ b/doxygen/manpages/man1/dcm2cda.1 @@ -1,4 +1,4 @@ -.TH "dcm2cda" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcm2cda" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcm2cda \- Extract CDA file from DICOM encapsulated CDA @@ -12,163 +12,10 @@ dcm2cda [options] dcmfile-in cdafile-out .PP .SH "DESCRIPTION" .PP -The \fBdcm2cda\fP utility reads a DICOM file of the Encapsulated CDA Storage SOP Class (\fIdcmfile-in\fP), extracts the embedded CDA document and writes it to an output file (\fIcdafile-out\fP)\&. Optionally a command can be executed after the creation of the CDA file\&. -.SH "PARAMETERS" -.PP -.PP -.nf -dcmfile-in DICOM input filename ("-" for stdin) - -cdafile-out CDA output filename -.fi -.PP -.SH "OPTIONS" -.PP -.SS "general options" -.PP -.nf - -h --help - print this help text and exit - - --version - print version information and exit - - --arguments - print expanded command line arguments - - -q --quiet - quiet mode, print no warnings and errors - - -v --verbose - verbose mode, print processing details - - -d --debug - debug mode, print debug information - - -ll --log-level [l]evel: string constant - (fatal, error, warn, info, debug, trace) - use level l for the logger - - -lc --log-config [f]ilename: string - use config file f for the logger -.fi -.PP -.SS "input options" -.PP -.nf -input file format: - - +f --read-file - read file format or data set (default) - - +fo --read-file-only - read file format only - - -f --read-dataset - read data set without file meta information - -input transfer syntax: - - -t= --read-xfer-auto - use TS recognition (default) - - -td --read-xfer-detect - ignore TS specified in the file meta header - - -te --read-xfer-little - read with explicit VR little endian TS - - -tb --read-xfer-big - read with explicit VR big endian TS - - -ti --read-xfer-implicit - read with implicit VR little endian TS - -parsing of odd-length attributes: - - +ao --accept-odd-length - accept odd length attributes (default) - - +ae --assume-even-length - assume real length is one byte larger - -handling of undefined length UN elements: - - +ui --enable-cp246 - read undefined len UN as implicit VR (default) - - -ui --disable-cp246 - read undefined len UN as explicit VR - -handling of defined length UN elements: - - -uc --retain-un - retain elements as UN (default) - - +uc --convert-un - convert to real VR if known - -automatic data correction: - - +dc --enable-correction - enable automatic data correction (default) - - -dc --disable-correction - disable automatic data correction - -bitstream format of deflated input: - - +bd --bitstream-deflated - expect deflated bitstream (default) - - +bz --bitstream-zlib - expect deflated zlib bitstream -.fi -.PP -.SH "LOGGING" -.PP -The level of logging output of the various command line tools and underlying libraries can be specified by the user\&. By default, only errors and warnings are written to the standard error stream\&. Using option \fI--verbose\fP also informational messages like processing details are reported\&. Option \fI--debug\fP can be used to get more details on the internal activity, e\&.g\&. for debugging purposes\&. Other logging levels can be selected using option \fI--log-level\fP\&. In \fI--quiet\fP mode only fatal errors are reported\&. In such very severe error events, the application will usually terminate\&. For more details on the different logging levels, see documentation of module 'oflog'\&. -.PP -In case the logging output should be written to file (optionally with logfile rotation), to syslog (Unix) or the event log (Windows) option \fI--log-config\fP can be used\&. This configuration file also allows for directing only certain messages to a particular output stream and for filtering certain messages based on the module or application where they are generated\&. An example configuration file is provided in \fI/logger\&.cfg\fP\&. -.SH "EXIT CODES" -.PP -The \fBdcm2cda\fP utility uses the following exit codes when terminating\&. This enables the user to check for the reason why the application terminated\&. -.SS "general" -.PP -.nf -EXITCODE_NO_ERROR 0 -EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 -.fi -.PP -.SS "input file errors" -.PP -.nf -EXITCODE_CANNOT_READ_INPUT_FILE 20 -EXITCODE_NO_INPUT_FILES 21 -EXITCODE_INVALID_INPUT_FILE 22 -.fi -.PP -.SS "output file errors" -.PP -.nf -EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 -.fi -.PP -.SH "COMMAND LINE" -.PP -All command line tools use the following notation for parameters: square brackets enclose optional values (0-1), three trailing dots indicate that multiple values are allowed (1-n), a combination of both means 0 to n values\&. -.PP -Command line options are distinguished from parameters by a leading '+' or '-' sign, respectively\&. Usually, order and position of command line options are arbitrary (i\&.e\&. they can appear anywhere)\&. However, if options are mutually exclusive the rightmost appearance is used\&. This behavior conforms to the standard evaluation rules of common Unix shells\&. -.PP -In addition, one or more command files can be specified using an '@' sign as a prefix to the filename (e\&.g\&. \fI@command\&.txt\fP)\&. Such a command argument is replaced by the content of the corresponding text file (multiple whitespaces are treated as a single separator unless they appear between two quotation marks) prior to any further evaluation\&. Please note that a command file cannot contain another command file\&. This simple but effective approach allows one to summarize common combinations of options/parameters and avoids longish and confusing command lines (an example is provided in file \fI/dumppat\&.txt\fP)\&. -.SH "ENVIRONMENT" -.PP -The \fBdcm2cda\fP utility will attempt to load DICOM data dictionaries specified in the \fIDCMDICTPATH\fP environment variable\&. By default, i\&.e\&. if the \fIDCMDICTPATH\fP environment variable is not set, the file \fI/dicom\&.dic\fP will be loaded unless the dictionary is built into the application (default for Windows)\&. -.PP -The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. +The \fBdcm2cda\fP tool is deprecated\&. Use \fBdcmdecap\fP instead, which supports the same command line parameters, and more\&. .SH "SEE ALSO" .PP -\fBcda2dcm\fP(1) +\fBdcmdecap\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2023-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2023-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcm2img.1 b/doxygen/manpages/man1/dcm2img.1 index e745b4f1..2b18b2f0 100644 --- a/doxygen/manpages/man1/dcm2img.1 +++ b/doxygen/manpages/man1/dcm2img.1 @@ -1,4 +1,4 @@ -.TH "dcm2img" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcm2img" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcm2img \- Convert DICOM image to standard image format @@ -171,6 +171,22 @@ color space conversion (JPEG compressed images only): +cn --conv-never never convert color space +bits stored: + + +bs --bits-stored-fix + correct inconsistent bits stored value (default) + + # If the value of BitsStored in the compressed bitstream is smaller + # than the value in the DICOM dataset, update the value in the dataset + # (JPEG compressed images only) + + -bs --bits-stored-keep + preserve inconsistent bits stored value + + # Keep the value of BitsStored even if inconsistent with the + # compressed bitstream. This may help in correctly decoding some + # defective images JPEG compressed images only) + workaround options for incorrect encodings (JPEG compressed images only): +w6 --workaround-pred6 @@ -555,4 +571,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBimg2dcm\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2001-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2001-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcm2json.1 b/doxygen/manpages/man1/dcm2json.1 index 46d7457e..3ad9e0da 100644 --- a/doxygen/manpages/man1/dcm2json.1 +++ b/doxygen/manpages/man1/dcm2json.1 @@ -1,4 +1,4 @@ -.TH "dcm2json" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcm2json" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcm2json \- Convert DICOM file and data set to JSON @@ -108,6 +108,27 @@ encoding of IS and DS (integer/decimal string) elements: -is --is-ds-string always encode as string + +bulk data URI options: + + -b --bulk-disabled + write everything as inline binary (default) + + +b --bulk-enabled + write large attributes as bulk data + + +bz --bulk-size [s]ize: integer (default: 1) + use bulk data for attributes >= s kBytes + + +bp --bulk-uri-prefix [u]ri prefix: string + use prefix u when generating bulk data URIs (default: file URI) + + +bd --bulk-dir [d]irectory: string + write bulk data files to d (default: '.') + + +bs --bulk-subdir + create subdirectory for each SOP instance + (default: no subdirectory) .fi .PP .SS "output options" @@ -291,13 +312,37 @@ The basic structure of the JSON output created from a DICOM file looks like the .fi .PP .SS "Bulk Data" -Binary data, i\&.e\&. DICOM element values with Value Representations (VR) of OB or OW, as well as OD, OF, OL, OV and UN values are always written as 'InlineBinary' (base64 encoding) to the JSON output\&. A future version of this tool might optionally use a 'BulkDataURI' instead, i\&.e\&. the WADO-RS URL of a bulk data item that contains the element value\&. This would be particularly useful for large amounts of data, such as pixel data\&. +By default, binary data, i\&.e\&. DICOM element values with Value Representations (VR) of OB, OD, OF, OL, OV, OW, and UN values are written as 'InlineBinary' (base64 encoding) to the JSON output\&. Option \fI--bulk-enabled\fP causes binary data as well as DS, FD, FL, IS, SV and UV to be replaced by 'BulkDataURI' values if the element value is larger than the cut-off threshold (default: 1 kByte)\&. The cut-off threshold can be specified with the \fI--bulk-size\fP option\&. The element values themselves are written as files to the directory given by the \fI--bulk-dir\fP option (default: current directory)\&. The filename is based on a SHA-256 checksum of the element value\&. By default, file URIs are generated that point to the bulk directory\&. For production use, a URI prefix for a WADO-RS service over which the element values can be retrieved should be specified using the \fI--bulk-uri-prefix\fP option\&. This can be implemented by configuring a web server that has read access to \fBdcm2json's\fP bulk directory\&. Finally, the option \fI--bulk-subdir\fP will cause a separate subdirectory to be created (and used in the bulk data URI) for each distinct SOP instance\&. +.PP +Note that the JSON syntax for the representation of encapsulated pixel data in 'InlineBinary' form is unspecified in DICOM, as is the JSON representation of encapsulated multi-frame pixel data\&. These DICOM files cannot be converted to JSON using \fBdcm2json\fP\&. +.PP +The file name extension for the bulk data files generated can be used to determine the MIME type that should be returned by the WADO-RS server: +.PP +.PP +.nf + Extension MIME Type + + .bin application/octet-stream + .jpeg image/jpeg + .dicom-rle image/dicom-rle + .jls image/jls + .jp2 image/jp2 + .jpx image/jpx + .jphc image/jphc + .jxl image/jxl + .mpeg video/mpeg + .mp4 video/mp4 + .H265 video/H265 +.fi +.PP +.PP +Finally, it should be noted that bulk data will be written 'as is', i\&.e\&. \fBdcm2json\fP will not attempt to validate or modify the element value in any way\&. For example, \fBdcm2json\fP will not add a JFIF header in the case of JPEG baseline compressed images, which some JPEG viewers may expect\&. .SH "NOTES" .PP .SS "Numbers as Strings" The DICOM standard allows certain numeric DICOM value representations, DS, IS, SV and UV, to be converted either to a JSON number or a JSON string\&. \fBdcm2json\fP converts DS and IS values to JSON numbers if they are valid decimal strings or integer strings, and to strings if they contain any illegal character\&. \fBdcm2json\fP converts SV and UV values to numbers if they are not larger than 9007199254740991ll or smaller than -9007199254740991ll, and to strings otherwise\&. While the JSON specification permits larger numbers, these are the largest integers that JavaScript can handle\&. Therefore, many JSON parsers cannot process larger numbers\&. .SS "Character Encoding" -As required by the DICOM JSON encoding, \fBdcm2json\fP always creates output in Unicode UTF-8 encoding and converts DICOM datasets accordingly\&. If this is not possible, for example because DCMTK has been compiled without character set conversion support, an error is returned\&. +As required by the DICOM JSON encoding, \fBdcm2json\fP always creates output in Unicode UTF-8 encoding and converts DICOM data sets accordingly\&. If this is not possible, for example because DCMTK has been compiled without character set conversion support, an error is returned\&. .SH "LOGGING" .PP The level of logging output of the various command line tools and underlying libraries can be specified by the user\&. By default, only errors and warnings are written to the standard error stream\&. Using option \fI--verbose\fP also informational messages like processing details are reported\&. Option \fI--debug\fP can be used to get more details on the internal activity, e\&.g\&. for debugging purposes\&. Other logging levels can be selected using option \fI--log-level\fP\&. In \fI--quiet\fP mode only fatal errors are reported\&. In such very severe error events, the application will usually terminate\&. For more details on the different logging levels, see documentation of module 'oflog'\&. @@ -351,4 +396,4 @@ The \fBdcm2json\fP utility will attempt to load character set mapping tables\&. The mapping table files are expected in DCMTK's \fI\fP\&. The \fIDCMICONVPATH\fP environment variable can be used to specify a different location\&. If a different location is specified, those mapping tables also replace any built-in tables\&. .SH "COPYRIGHT" .PP -Copyright (C) 2016-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2016-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcm2pdf.1 b/doxygen/manpages/man1/dcm2pdf.1 index 3385aa45..73b3529e 100644 --- a/doxygen/manpages/man1/dcm2pdf.1 +++ b/doxygen/manpages/man1/dcm2pdf.1 @@ -1,4 +1,4 @@ -.TH "dcm2pdf" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcm2pdf" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcm2pdf \- Extract PDF file from DICOM encapsulated PDF @@ -12,182 +12,10 @@ dcm2pdf [options] dcmfile-in pdffile-out .PP .SH "DESCRIPTION" .PP -The \fBdcm2pdf\fP utility reads a DICOM file of the Encapsulated PDF Storage SOP Class (\fIdcmfile-in\fP), extracts the embedded PDF document and writes it to an output file (\fIpdffile-out\fP)\&. Optionally a command can be executed after the creation of the PDF file\&. -.SH "PARAMETERS" -.PP -.PP -.nf -dcmfile-in DICOM input filename ("-" for stdin) - -pdffile-out PDF output filename -.fi -.PP -.SH "OPTIONS" -.PP -.SS "general options" -.PP -.nf - -h --help - print this help text and exit - - --version - print version information and exit - - --arguments - print expanded command line arguments - - -q --quiet - quiet mode, print no warnings and errors - - -v --verbose - verbose mode, print processing details - - -d --debug - debug mode, print debug information - - -ll --log-level [l]evel: string constant - (fatal, error, warn, info, debug, trace) - use level l for the logger - - -lc --log-config [f]ilename: string - use config file f for the logger -.fi -.PP -.SS "input options" -.PP -.nf -input file format: - - +f --read-file - read file format or data set (default) - - +fo --read-file-only - read file format only - - -f --read-dataset - read data set without file meta information - -input transfer syntax: - - -t= --read-xfer-auto - use TS recognition (default) - - -td --read-xfer-detect - ignore TS specified in the file meta header - - -te --read-xfer-little - read with explicit VR little endian TS - - -tb --read-xfer-big - read with explicit VR big endian TS - - -ti --read-xfer-implicit - read with implicit VR little endian TS - -parsing of odd-length attributes: - - +ao --accept-odd-length - accept odd length attributes (default) - - +ae --assume-even-length - assume real length is one byte larger - -handling of undefined length UN elements: - - +ui --enable-cp246 - read undefined len UN as implicit VR (default) - - -ui --disable-cp246 - read undefined len UN as explicit VR - -handling of defined length UN elements: - - -uc --retain-un - retain elements as UN (default) - - +uc --convert-un - convert to real VR if known - -automatic data correction: - - +dc --enable-correction - enable automatic data correction (default) - - -dc --disable-correction - disable automatic data correction - -bitstream format of deflated input: - - +bd --bitstream-deflated - expect deflated bitstream (default) - - +bz --bitstream-zlib - expect deflated zlib bitstream -.fi -.PP -.SS "processing options" -execution options: -.PP -.nf - -x --exec [c]ommand: string - execute command c after PDF extraction - -.fi -.PP -.SH "NOTES" -.PP -Option \fI--exec\fP allows for the execution of a certain command line after the creation of the PDF document\&. The command line to be executed is passed to this option as a parameter\&. The specified command line may contain the placeholder '#f', which will be replaced by the PDF filename at run time\&. The specified command line is executed in the foreground, i\&.e\&. \fBpdf2dcm\fP will be blocked until the command terminates\&. -.SH "LOGGING" -.PP -The level of logging output of the various command line tools and underlying libraries can be specified by the user\&. By default, only errors and warnings are written to the standard error stream\&. Using option \fI--verbose\fP also informational messages like processing details are reported\&. Option \fI--debug\fP can be used to get more details on the internal activity, e\&.g\&. for debugging purposes\&. Other logging levels can be selected using option \fI--log-level\fP\&. In \fI--quiet\fP mode only fatal errors are reported\&. In such very severe error events, the application will usually terminate\&. For more details on the different logging levels, see documentation of module 'oflog'\&. -.PP -In case the logging output should be written to file (optionally with logfile rotation), to syslog (Unix) or the event log (Windows) option \fI--log-config\fP can be used\&. This configuration file also allows for directing only certain messages to a particular output stream and for filtering certain messages based on the module or application where they are generated\&. An example configuration file is provided in \fI/logger\&.cfg\fP\&. -.SH "EXIT CODES" -.PP -The \fBdcm2pdf\fP utility uses the following exit codes when terminating\&. This enables the user to check for the reason why the application terminated\&. -.SS "general" -.PP -.nf -EXITCODE_NO_ERROR 0 -EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 -.fi -.PP -.SS "input file errors" -.PP -.nf -EXITCODE_CANNOT_READ_INPUT_FILE 20 -EXITCODE_NO_INPUT_FILES 21 -EXITCODE_INVALID_INPUT_FILE 22 -.fi -.PP -.SS "output file errors" -.PP -.nf -EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 -.fi -.PP -.SS "processing errors" -.PP -.nf -EXITCODE_CANNOT_CONVERT_TO_UNICODE 80 -EXITCODE_CANNOT_WRITE_VALID_JSON 81 -.fi -.PP -.SH "COMMAND LINE" -.PP -All command line tools use the following notation for parameters: square brackets enclose optional values (0-1), three trailing dots indicate that multiple values are allowed (1-n), a combination of both means 0 to n values\&. -.PP -Command line options are distinguished from parameters by a leading '+' or '-' sign, respectively\&. Usually, order and position of command line options are arbitrary (i\&.e\&. they can appear anywhere)\&. However, if options are mutually exclusive the rightmost appearance is used\&. This behavior conforms to the standard evaluation rules of common Unix shells\&. -.PP -In addition, one or more command files can be specified using an '@' sign as a prefix to the filename (e\&.g\&. \fI@command\&.txt\fP)\&. Such a command argument is replaced by the content of the corresponding text file (multiple whitespaces are treated as a single separator unless they appear between two quotation marks) prior to any further evaluation\&. Please note that a command file cannot contain another command file\&. This simple but effective approach allows one to summarize common combinations of options/parameters and avoids longish and confusing command lines (an example is provided in file \fI/dumppat\&.txt\fP)\&. -.SH "ENVIRONMENT" -.PP -The \fBdcm2pdf\fP utility will attempt to load DICOM data dictionaries specified in the \fIDCMDICTPATH\fP environment variable\&. By default, i\&.e\&. if the \fIDCMDICTPATH\fP environment variable is not set, the file \fI/dicom\&.dic\fP will be loaded unless the dictionary is built into the application (default for Windows)\&. -.PP -The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. +The \fBdcm2pdf\fP tool is deprecated\&. Use \fBdcmdecap\fP instead, which supports the same command line parameters, and more\&. .SH "SEE ALSO" .PP -\fBpdf2dcm\fP(1) +\fBdcmdecap\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2007-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2007-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcm2pnm.1 b/doxygen/manpages/man1/dcm2pnm.1 index 47c9a4f9..fd13c56a 100644 --- a/doxygen/manpages/man1/dcm2pnm.1 +++ b/doxygen/manpages/man1/dcm2pnm.1 @@ -1,4 +1,4 @@ -.TH "dcm2pnm" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcm2pnm" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcm2pnm \- Convert DICOM images to PGM/PPM, PNG, TIFF or BMP @@ -18,4 +18,4 @@ The \fBdcm2pnm\fP tool is deprecated\&. Use \fBdcm2img\fP instead, which support \fBdcm2img\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1998-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1998-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcm2xml.1 b/doxygen/manpages/man1/dcm2xml.1 index df707083..220814f5 100644 --- a/doxygen/manpages/man1/dcm2xml.1 +++ b/doxygen/manpages/man1/dcm2xml.1 @@ -1,4 +1,4 @@ -.TH "dcm2xml" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcm2xml" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcm2xml \- Convert DICOM file and data set to XML @@ -293,4 +293,4 @@ The mapping table files are expected in DCMTK's \fI\fP\&. The \fIDCMICO \fBxml2dcm\fP(1), \fBdcmconv\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2002-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2002-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmcjpeg.1 b/doxygen/manpages/man1/dcmcjpeg.1 index 3fefcd19..d73b9bd8 100644 --- a/doxygen/manpages/man1/dcmcjpeg.1 +++ b/doxygen/manpages/man1/dcmcjpeg.1 @@ -1,4 +1,4 @@ -.TH "dcmcjpeg" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmcjpeg" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmcjpeg \- Encode DICOM file to JPEG transfer syntax @@ -647,4 +647,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmdjpeg\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2001-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2001-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmcjpls.1 b/doxygen/manpages/man1/dcmcjpls.1 index 88a08f2b..2c66fb4f 100644 --- a/doxygen/manpages/man1/dcmcjpls.1 +++ b/doxygen/manpages/man1/dcmcjpls.1 @@ -1,4 +1,4 @@ -.TH "dcmcjpls" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmcjpls" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmcjpls \- Encode DICOM file to JPEG-LS transfer syntax @@ -339,4 +339,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmdjpls\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2009-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2009-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmconv.1 b/doxygen/manpages/man1/dcmconv.1 index 26b80c68..934e30f4 100644 --- a/doxygen/manpages/man1/dcmconv.1 +++ b/doxygen/manpages/man1/dcmconv.1 @@ -1,4 +1,4 @@ -.TH "dcmconv" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmconv" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmconv \- Convert DICOM file encoding @@ -351,4 +351,4 @@ The mapping table files are expected in DCMTK's \fI\fP\&. The \fIDCMICO \fBdcmdump\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1994-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1994-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmcrle.1 b/doxygen/manpages/man1/dcmcrle.1 index fc4f3981..f392ae00 100644 --- a/doxygen/manpages/man1/dcmcrle.1 +++ b/doxygen/manpages/man1/dcmcrle.1 @@ -1,4 +1,4 @@ -.TH "dcmcrle" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmcrle" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmcrle \- Encode DICOM file to RLE transfer syntax @@ -212,4 +212,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmdrle\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2002-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2002-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmdecap.1 b/doxygen/manpages/man1/dcmdecap.1 new file mode 100644 index 00000000..e7f1cb43 --- /dev/null +++ b/doxygen/manpages/man1/dcmdecap.1 @@ -0,0 +1,192 @@ +.TH "dcmdecap" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- +.nh +.SH NAME +dcmdecap \- Extract encapsulated file from DICOM encapsulated storage object + +.SH "SYNOPSIS" +.PP +.PP +.nf +dcmdecap [options] dcmfile-in encfile-out +.fi +.PP +.SH "DESCRIPTION" +.PP +The \fBdcmdecap\fP utility reads a DICOM file of one of the Encapsulated Storage SOP Classes (\fIdcmfile-in\fP), extracts the embedded document and writes it to an output file (\fIencfile-out\fP)\&. Optionally a command can be executed after the creation of the output file\&. +.SH "PARAMETERS" +.PP +.PP +.nf +dcmfile-in DICOM input filename ("-" for stdin) + +encfile-out Encapsulated document output filename ("-" for stdout) +.fi +.PP +.SH "OPTIONS" +.PP +.SS "general options" +.PP +.nf + -h --help + print this help text and exit + + --version + print version information and exit + + --arguments + print expanded command line arguments + + -q --quiet + quiet mode, print no warnings and errors + + -v --verbose + verbose mode, print processing details + + -d --debug + debug mode, print debug information + + -ll --log-level [l]evel: string constant + (fatal, error, warn, info, debug, trace) + use level l for the logger + + -lc --log-config [f]ilename: string + use config file f for the logger +.fi +.PP +.SS "input options" +.PP +.nf +input file format: + + +f --read-file + read file format or data set (default) + + +fo --read-file-only + read file format only + + -f --read-dataset + read data set without file meta information + +input transfer syntax: + + -t= --read-xfer-auto + use TS recognition (default) + + -td --read-xfer-detect + ignore TS specified in the file meta header + + -te --read-xfer-little + read with explicit VR little endian TS + + -tb --read-xfer-big + read with explicit VR big endian TS + + -ti --read-xfer-implicit + read with implicit VR little endian TS + +parsing of odd-length attributes: + + +ao --accept-odd-length + accept odd length attributes (default) + + +ae --assume-even-length + assume real length is one byte larger + +handling of undefined length UN elements: + + +ui --enable-cp246 + read undefined len UN as implicit VR (default) + + -ui --disable-cp246 + read undefined len UN as explicit VR + +handling of defined length UN elements: + + -uc --retain-un + retain elements as UN (default) + + +uc --convert-un + convert to real VR if known + +automatic data correction: + + +dc --enable-correction + enable automatic data correction (default) + + -dc --disable-correction + disable automatic data correction + +bitstream format of deflated input: + + +bd --bitstream-deflated + expect deflated bitstream (default) + + +bz --bitstream-zlib + expect deflated zlib bitstream +.fi +.PP +.SS "processing options" +execution options: +.PP +.nf + -x --exec [c]ommand: string + execute command c after document extraction + +.fi +.PP +.SH "NOTES" +.PP +Option \fI--exec\fP allows for the execution of a certain command line after the creation of the PDF document\&. The command line to be executed is passed to this option as a parameter\&. The specified command line may contain the placeholder '#f', which will be replaced by the output filename at run time\&. The specified command line is executed in the foreground, i\&.e\&. \fBdcmdecap\fP will be blocked until the command terminates\&. +.SH "LOGGING" +.PP +The level of logging output of the various command line tools and underlying libraries can be specified by the user\&. By default, only errors and warnings are written to the standard error stream\&. Using option \fI--verbose\fP also informational messages like processing details are reported\&. Option \fI--debug\fP can be used to get more details on the internal activity, e\&.g\&. for debugging purposes\&. Other logging levels can be selected using option \fI--log-level\fP\&. In \fI--quiet\fP mode only fatal errors are reported\&. In such very severe error events, the application will usually terminate\&. For more details on the different logging levels, see documentation of module 'oflog'\&. +.PP +In case the logging output should be written to file (optionally with logfile rotation), to syslog (Unix) or the event log (Windows) option \fI--log-config\fP can be used\&. This configuration file also allows for directing only certain messages to a particular output stream and for filtering certain messages based on the module or application where they are generated\&. An example configuration file is provided in \fI/logger\&.cfg\fP\&. +.SH "EXIT CODES" +.PP +The \fBdcmdecap\fP utility uses the following exit codes when terminating\&. This enables the user to check for the reason why the application terminated\&. +.SS "general" +.PP +.nf +EXITCODE_NO_ERROR 0 +EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 +.fi +.PP +.SS "input file errors" +.PP +.nf +EXITCODE_CANNOT_READ_INPUT_FILE 20 +EXITCODE_NO_INPUT_FILES 21 +EXITCODE_INVALID_INPUT_FILE 22 +.fi +.PP +.SS "output file errors" +.PP +.nf +EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 +.fi +.PP +.SS "processing errors" +.PP +.nf +EXITCODE_EXEC_FAILED 91 +.fi +.PP +.SH "COMMAND LINE" +.PP +All command line tools use the following notation for parameters: square brackets enclose optional values (0-1), three trailing dots indicate that multiple values are allowed (1-n), a combination of both means 0 to n values\&. +.PP +Command line options are distinguished from parameters by a leading '+' or '-' sign, respectively\&. Usually, order and position of command line options are arbitrary (i\&.e\&. they can appear anywhere)\&. However, if options are mutually exclusive the rightmost appearance is used\&. This behavior conforms to the standard evaluation rules of common Unix shells\&. +.PP +In addition, one or more command files can be specified using an '@' sign as a prefix to the filename (e\&.g\&. \fI@command\&.txt\fP)\&. Such a command argument is replaced by the content of the corresponding text file (multiple whitespaces are treated as a single separator unless they appear between two quotation marks) prior to any further evaluation\&. Please note that a command file cannot contain another command file\&. This simple but effective approach allows one to summarize common combinations of options/parameters and avoids longish and confusing command lines (an example is provided in file \fI/dumppat\&.txt\fP)\&. +.SH "ENVIRONMENT" +.PP +The \fBdcmdecap\fP utility will attempt to load DICOM data dictionaries specified in the \fIDCMDICTPATH\fP environment variable\&. By default, i\&.e\&. if the \fIDCMDICTPATH\fP environment variable is not set, the file \fI/dicom\&.dic\fP will be loaded unless the dictionary is built into the application (default for Windows)\&. +.PP +The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. +.SH "SEE ALSO" +.PP +\fBdcmencap\fP(1) +.SH "COPYRIGHT" +.PP +Copyright (C) 2007-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmdjpeg.1 b/doxygen/manpages/man1/dcmdjpeg.1 index cedf8f20..f26636e0 100644 --- a/doxygen/manpages/man1/dcmdjpeg.1 +++ b/doxygen/manpages/man1/dcmdjpeg.1 @@ -1,4 +1,4 @@ -.TH "dcmdjpeg" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmdjpeg" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmdjpeg \- Decode JPEG-compressed DICOM file @@ -144,6 +144,21 @@ planar configuration: # If the compressed image is a color image, store in color-by-plane # planar configuration. +bits stored: + + +bs --bits-stored-fix + correct inconsistent bits stored value (default) + + # If the value of BitsStored in the compressed bitstream is smaller + # than the value in the DICOM dataset, update the value in the dataset. + + -bs --bits-stored-keep + preserve inconsistent bits stored value + + # Keep the value of BitsStored even if inconsistent with the + # compressed bitstream. This may help in correctly decoding some + # defective images. + SOP Instance UID: +ud --uid-default @@ -302,4 +317,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmcjpeg\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2001-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2001-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmdjpls.1 b/doxygen/manpages/man1/dcmdjpls.1 index 42faf574..99d80132 100644 --- a/doxygen/manpages/man1/dcmdjpls.1 +++ b/doxygen/manpages/man1/dcmdjpls.1 @@ -1,4 +1,4 @@ -.TH "dcmdjpls" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmdjpls" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmdjpls \- Decode JPEG-LS compressed DICOM file @@ -240,4 +240,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmcjpls\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2009-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2009-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmdrle.1 b/doxygen/manpages/man1/dcmdrle.1 index baf40c1e..738c41e2 100644 --- a/doxygen/manpages/man1/dcmdrle.1 +++ b/doxygen/manpages/man1/dcmdrle.1 @@ -1,4 +1,4 @@ -.TH "dcmdrle" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmdrle" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmdrle \- Decode RLE-compressed DICOM file @@ -206,4 +206,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmcrle\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2002-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany +Copyright (C) 2002-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany diff --git a/doxygen/manpages/man1/dcmdspfn.1 b/doxygen/manpages/man1/dcmdspfn.1 index 979780e4..fab122f3 100644 --- a/doxygen/manpages/man1/dcmdspfn.1 +++ b/doxygen/manpages/man1/dcmdspfn.1 @@ -1,4 +1,4 @@ -.TH "dcmdspfn" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmdspfn" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmdspfn \- Export standard display curves to a text file @@ -143,4 +143,4 @@ In addition, one or more command files can be specified using an '@' sign as a p \fBdconvlum\fP(1), \fBdcod2lum\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1999-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1999-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmdump.1 b/doxygen/manpages/man1/dcmdump.1 index 7e75a0b2..51530409 100644 --- a/doxygen/manpages/man1/dcmdump.1 +++ b/doxygen/manpages/man1/dcmdump.1 @@ -1,4 +1,4 @@ -.TH "dcmdump" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmdump" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmdump \- Dump DICOM file and data set @@ -364,4 +364,4 @@ The mapping table files are expected in DCMTK's \fI\fP\&. The \fIDCMICO \fBdump2dcm\fP(1), \fBdcmconv\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1994-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1994-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmencap.1 b/doxygen/manpages/man1/dcmencap.1 new file mode 100644 index 00000000..f157caab --- /dev/null +++ b/doxygen/manpages/man1/dcmencap.1 @@ -0,0 +1,271 @@ +.TH "dcmencap" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- +.nh +.SH NAME +dcmencap \- Encapsulate document into DICOM format + +.SH "SYNOPSIS" +.PP +.PP +.nf +dcmencap [options] docfile-in dcmfile-out +.fi +.PP +.SH "DESCRIPTION" +.PP +The \fBdcmencap\fP utility reads a document file in one of the supported file formats, converts it to a SOP instance of the corresponding DICOM Encapsulated Storage SOP Class and stores the converted data in an output file (\fIdcmfile-out\fP)\&. +.SH "PARAMETERS" +.PP +.PP +.nf +docfile-in input filename to be converted + +dcmfile-out DICOM output filename ("-" for stdout) +.fi +.PP +.SH "OPTIONS" +.PP +.SS "general options" +.PP +.nf + -h --help + print this help text and exit + + --version + print version information and exit + + --arguments + print expanded command line arguments + + -q --quiet + quiet mode, print no warnings and errors + + -v --verbose + verbose mode, print processing details + + -d --debug + debug mode, print debug information + + -ll --log-level [l]evel: string constant + (fatal, error, warn, info, debug, trace) + use level l for the logger + + -lc --log-config [f]ilename: string + use config file f for the logger +.fi +.PP +.SS "input options" +.PP +.nf +input file format: + + +fa --filetype-auto + automatically determine file type (default) + + +fp --filetype-pdf + expect PDF file + + +fc --filetype-cda + expect CDA file + + +fs --filetype-stl + expect STL file + + +fm --filetype-mtl + expect MTL file + + +fo --filetype-obj + expect OBJ file +.fi +.PP +.SS "DICOM document options" +.PP +.nf +document title: + + +t --title [t]itle: string (default: empty) + document title + + +cn --concept-name [CSD] [CV] [CM]: string (default: empty) + coded representation of document title defined by coding + scheme designator CSD, code value CV and code meaning CM + +patient data: + + +pn --patient-name [n]ame: string + patient's name in DICOM PN syntax + + +pi --patient-id [i]d: string + patient identifier + + +pb --patient-birthdate [d]ate: string (YYYYMMDD) + patient's birth date + + +ps --patient-sex [s]ex: string (M, F or O) + patient's sex + +device data: + + +mn --manufacturer [n]ame: string + manufacturer's name + + +mm --manufacturer-model [n]ame: string + manufacturer's model name + + +ds --device-serial [n]umber: string + device serial number + + +sv --software-versions [v]ersions: string + software versions + +manufacturing 3d model data (STL/MTL/OBJ only): + + +mu --measurement-units [CSD] [CV] [CM]: string + measurement units with coding scheme designator CSD, + code value CV and code meaning CM (default: UCUM, um, um) + +study and series: + + +sg --generate + generate new study and series UIDs (default) + + +st --study-from [f]ilename: string + read patient/study data from DICOM file + + +se --series-from [f]ilename: string + read patient/study/series data from DICOM file + +instance number: + + +i1 --instance-one + use instance number 1 (default, not with +se) + + +ii --instance-inc + increment instance number (only with +se) + + +is --instance-set [i]nstance number: integer + use instance number i + +burned-in annotation: + + +an --annotation-yes + document contains patient identifying data (default) + + -an --annotation-no + document does not contain patient identifying data +.fi +.PP +.SS "processing options" +.PP +.nf +CDA processing options: + + -ov --no-override + CDA patient and document data must match study, + series or manually entered information (default) + + +ov --override + CDA's data will be overwritten by study, series + or manually entered information + +other processing options: + + -k --key [k]ey: gggg,eeee="str", path or dictionary name="str" + add further attribute +.fi +.PP +.SS "output options" +.PP +.nf +output transfer syntax: + +te --write-xfer-little + write with explicit VR little endian (default) + + +tb --write-xfer-big + write with explicit VR big endian TS + + +ti --write-xfer-implicit + write with implicit VR little endian TS + +group length encoding: + + -g --group-length-remove + write without group length elements (default) + + +g --group-length-create + write with group length elements + +length encoding in sequences and items: + + +e --length-explicit + write with explicit lengths (default) + + -e --length-undefined + write with undefined lengths + +data set trailing padding (not with --write-dataset): + + -p --padding-off + no padding (implicit if --write-dataset) + + +p --padding-create [f]ile-pad [i]tem-pad: integer + align file on multiple of f bytes + and items on multiple of i bytes +.fi +.PP +.SH "NOTES" +.PP +.SS "Attribute Sources" +The application may be fed with some additional input for filling mandatory (and optional) attributes in the new DICOM file like patient, study and series information: +.PP +.IP "\(bu" 2 +The \fI--key\fP option can be used to add further attributes to the DICOM output file\&. +.IP "\(bu" 2 +It is also possible to specify sequences, items and nested attributes using the \fI--key\fP option\&. In these cases, a special 'path' notation has to be used\&. Details on this path notation can be found in the documentation of \fBdcmodify\fP\&. +.IP "\(bu" 2 +The \fI--key\fP option can be present more than once\&. +.IP "\(bu" 2 +The value part (after the '=') may be absent causing the attribute to be set with zero length\&. +.IP "\(bu" 2 +Please be advised that the \fI--key\fP option is applied at the very end, just before saving the DICOM file, so there is no value checking whatsoever\&. +.PP +.SH "LOGGING" +.PP +The level of logging output of the various command line tools and underlying libraries can be specified by the user\&. By default, only errors and warnings are written to the standard error stream\&. Using option \fI--verbose\fP also informational messages like processing details are reported\&. Option \fI--debug\fP can be used to get more details on the internal activity, e\&.g\&. for debugging purposes\&. Other logging levels can be selected using option \fI--log-level\fP\&. In \fI--quiet\fP mode only fatal errors are reported\&. In such very severe error events, the application will usually terminate\&. For more details on the different logging levels, see documentation of module 'oflog'\&. +.PP +In case the logging output should be written to file (optionally with logfile rotation), to syslog (Unix) or the event log (Windows) option \fI--log-config\fP can be used\&. This configuration file also allows for directing only certain messages to a particular output stream and for filtering certain messages based on the module or application where they are generated\&. An example configuration file is provided in \fI/logger\&.cfg\fP\&. +.SH "COMMAND LINE" +.PP +All command line tools use the following notation for parameters: square brackets enclose optional values (0-1), three trailing dots indicate that multiple values are allowed (1-n), a combination of both means 0 to n values\&. +.PP +Command line options are distinguished from parameters by a leading '+' or '-' sign, respectively\&. Usually, order and position of command line options are arbitrary (i\&.e\&. they can appear anywhere)\&. However, if options are mutually exclusive the rightmost appearance is used\&. This behavior conforms to the standard evaluation rules of common Unix shells\&. +.PP +In addition, one or more command files can be specified using an '@' sign as a prefix to the filename (e\&.g\&. \fI@command\&.txt\fP)\&. Such a command argument is replaced by the content of the corresponding text file (multiple whitespaces are treated as a single separator unless they appear between two quotation marks) prior to any further evaluation\&. Please note that a command file cannot contain another command file\&. This simple but effective approach allows one to summarize common combinations of options/parameters and avoids longish and confusing command lines (an example is provided in file \fI/dumppat\&.txt\fP)\&. +.SH "EXIT CODES" +.PP +The \fBdcmencap\fP utility uses the following exit codes when terminating\&. This enables the user to check for the reason why the application terminated\&. +.SS "general" +.PP +.nf +EXITCODE_NO_ERROR 0 +.fi +.PP +.SS "input file errors" +.PP +.nf +EXITCODE_INVALID_INPUT_FILE 22 +.fi +.PP +.SS "output file errors" +.PP +.nf +EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 +.fi +.PP +.SH "ENVIRONMENT" +.PP +The \fBdcmencap\fP utility will attempt to load DICOM data dictionaries specified in the \fIDCMDICTPATH\fP environment variable\&. By default, i\&.e\&. if the \fIDCMDICTPATH\fP environment variable is not set, the file \fI/dicom\&.dic\fP will be loaded unless the dictionary is built into the application (default for Windows)\&. +.PP +The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. +.SH "COPYRIGHT" +.PP +Copyright (C) 2018-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmftest.1 b/doxygen/manpages/man1/dcmftest.1 index a7ff3aea..5d5f3932 100644 --- a/doxygen/manpages/man1/dcmftest.1 +++ b/doxygen/manpages/man1/dcmftest.1 @@ -1,4 +1,4 @@ -.TH "dcmftest" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmftest" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmftest \- Test if file uses DICOM part 10 format @@ -28,4 +28,4 @@ All files specified on the command line are checked for the presence of the DICO \fBdcmgpdir\fP(1), \fBdcmmkdir\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1997-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1997-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmgpdir.1 b/doxygen/manpages/man1/dcmgpdir.1 index 47c328fb..a2770dc4 100644 --- a/doxygen/manpages/man1/dcmgpdir.1 +++ b/doxygen/manpages/man1/dcmgpdir.1 @@ -1,4 +1,4 @@ -.TH "dcmgpdir" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmgpdir" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmgpdir \- Create a general purpose DICOMDIR @@ -18,4 +18,4 @@ The \fBdcmgpdir\fP tool is deprecated\&. Use \fBdcmmkdir\fP instead, which suppo \fBdcmmkdir\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1996-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1996-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmicmp.1 b/doxygen/manpages/man1/dcmicmp.1 index 6e92333a..f036989d 100644 --- a/doxygen/manpages/man1/dcmicmp.1 +++ b/doxygen/manpages/man1/dcmicmp.1 @@ -1,4 +1,4 @@ -.TH "dcmicmp" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmicmp" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmicmp \- Compare DICOM images and compute difference metrics @@ -300,4 +300,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcm2pnm\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2018-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2018-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmj2pnm.1 b/doxygen/manpages/man1/dcmj2pnm.1 index 8acd3984..ff18cfe1 100644 --- a/doxygen/manpages/man1/dcmj2pnm.1 +++ b/doxygen/manpages/man1/dcmj2pnm.1 @@ -1,4 +1,4 @@ -.TH "dcmj2pnm" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmj2pnm" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmj2pnm \- Convert DICOM images to PGM/PPM, PNG, TIFF, JPEG or BMP @@ -18,4 +18,4 @@ The \fBdcmj2pnm\fP tool is deprecated\&. Use \fBdcm2img\fP instead, which suppor \fBdcm2img\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2001-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2001-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcml2pnm.1 b/doxygen/manpages/man1/dcml2pnm.1 index 68f1beab..564c5bb4 100644 --- a/doxygen/manpages/man1/dcml2pnm.1 +++ b/doxygen/manpages/man1/dcml2pnm.1 @@ -1,4 +1,4 @@ -.TH "dcml2pnm" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcml2pnm" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcml2pnm \- Convert DICOM images to PGM/PPM, PNG, TIFF or BMP @@ -18,4 +18,4 @@ The \fBdcml2pnm\fP tool is deprecated\&. Use \fBdcm2img\fP instead, which suppor \fBdcm2img\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2001-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2001-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmmkcrv.1 b/doxygen/manpages/man1/dcmmkcrv.1 index 2b9ce5f3..66829581 100644 --- a/doxygen/manpages/man1/dcmmkcrv.1 +++ b/doxygen/manpages/man1/dcmmkcrv.1 @@ -1,4 +1,4 @@ -.TH "dcmmkcrv" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmmkcrv" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmmkcrv \- Add 2D curve data to image @@ -125,4 +125,4 @@ The \fBdcmmkcrv\fP utility will attempt to load DICOM data dictionaries specifie The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. .SH "COPYRIGHT" .PP -Copyright (C) 1998-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1998-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmmkdir.1 b/doxygen/manpages/man1/dcmmkdir.1 index 51b77326..02ddab24 100644 --- a/doxygen/manpages/man1/dcmmkdir.1 +++ b/doxygen/manpages/man1/dcmmkdir.1 @@ -1,4 +1,4 @@ -.TH "dcmmkdir" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmmkdir" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmmkdir \- Create a DICOMDIR file @@ -429,4 +429,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmgpdir\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2001-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2001-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmmklut.1 b/doxygen/manpages/man1/dcmmklut.1 index a8880ea1..291bbe96 100644 --- a/doxygen/manpages/man1/dcmmklut.1 +++ b/doxygen/manpages/man1/dcmmklut.1 @@ -1,4 +1,4 @@ -.TH "dcmmklut" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmmklut" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmmklut \- Create DICOM look-up tables @@ -189,4 +189,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fI/philips\&.lut\fP - sample LUT in text format .SH "COPYRIGHT" .PP -Copyright (C) 1998-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1998-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmodify.1 b/doxygen/manpages/man1/dcmodify.1 index f884b399..2281cc97 100644 --- a/doxygen/manpages/man1/dcmodify.1 +++ b/doxygen/manpages/man1/dcmodify.1 @@ -1,4 +1,4 @@ -.TH "dcmodify" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmodify" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmodify \- Modify DICOM files @@ -442,4 +442,4 @@ The \fBdcmodify\fP utility will attempt to load DICOM data dictionaries specifie The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. .SH "COPYRIGHT" .PP -Copyright (C) 2003-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2003-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmp2pgm.1 b/doxygen/manpages/man1/dcmp2pgm.1 index af059f71..f53e5f37 100644 --- a/doxygen/manpages/man1/dcmp2pgm.1 +++ b/doxygen/manpages/man1/dcmp2pgm.1 @@ -1,4 +1,4 @@ -.TH "dcmp2pgm" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmp2pgm" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmp2pgm \- Read DICOM image and presentation state and render bitmap @@ -111,4 +111,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fI/dcmpstat\&.cfg\fP - sample configuration file .SH "COPYRIGHT" .PP -Copyright (C) 1998-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1998-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmprscp.1 b/doxygen/manpages/man1/dcmprscp.1 index 9ede33c5..6a49adfb 100644 --- a/doxygen/manpages/man1/dcmprscp.1 +++ b/doxygen/manpages/man1/dcmprscp.1 @@ -1,4 +1,4 @@ -.TH "dcmprscp" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmprscp" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmprscp \- DICOM basic grayscale print management SCP @@ -90,4 +90,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmprscu\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1999-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1999-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmprscu.1 b/doxygen/manpages/man1/dcmprscu.1 index 0b514a6b..ee41ce41 100644 --- a/doxygen/manpages/man1/dcmprscu.1 +++ b/doxygen/manpages/man1/dcmprscu.1 @@ -1,4 +1,4 @@ -.TH "dcmprscu" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmprscu" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmprscu \- Print spooler for presentation state viewer @@ -147,4 +147,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmprscp\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1999-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1999-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmpschk.1 b/doxygen/manpages/man1/dcmpschk.1 index 3e5f7d5d..94c915a4 100644 --- a/doxygen/manpages/man1/dcmpschk.1 +++ b/doxygen/manpages/man1/dcmpschk.1 @@ -1,4 +1,4 @@ -.TH "dcmpschk" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmpschk" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmpschk \- Checking tool for presentation states @@ -59,6 +59,21 @@ dcmfile-in presentation state file(s) to be checked use config file f for the logger .fi .PP +.SS "validation options" +.PP +.nf + --validate-std + images referenced by GSPS must belong to the + same SOP class (default) + + --validate-related + images referenced by GSPS may belong to related + 'for presentation' and 'for processing' SOP class + + --validate-relaxed + images referenced by GSPS may be any SOP class +.fi +.PP .SH "LOGGING" .PP The level of logging output of the various command line tools and underlying libraries can be specified by the user\&. By default, only errors and warnings are written to the standard error stream\&. Using option \fI--verbose\fP also informational messages like processing details are reported\&. Option \fI--debug\fP can be used to get more details on the internal activity, e\&.g\&. for debugging purposes\&. Other logging levels can be selected using option \fI--log-level\fP\&. In \fI--quiet\fP mode only fatal errors are reported\&. In such very severe error events, the application will usually terminate\&. For more details on the different logging levels, see documentation of module 'oflog'\&. @@ -78,4 +93,4 @@ The \fBdcmpschk\fP utility will attempt to load DICOM data dictionaries specifie The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. .SH "COPYRIGHT" .PP -Copyright (C) 2000-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2000-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmpsmk.1 b/doxygen/manpages/man1/dcmpsmk.1 index 2988b13e..3b0aa578 100644 --- a/doxygen/manpages/man1/dcmpsmk.1 +++ b/doxygen/manpages/man1/dcmpsmk.1 @@ -1,4 +1,4 @@ -.TH "dcmpsmk" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmpsmk" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmpsmk \- Create DICOM grayscale softcopy presentation state @@ -157,6 +157,21 @@ location of referenced image: image located on storage medium .fi .PP +.SS "validation options" +.PP +.nf + --validate-std + images referenced by GSPS must belong to the + same SOP class (default) + + --validate-related + images referenced by GSPS may belong to related + 'for presentation' and 'for processing' SOP class + + --validate-relaxed + images referenced by GSPS may be any SOP class +.fi +.PP .SS "output options" .PP .nf @@ -197,4 +212,4 @@ The \fBdcmpsmk\fP utility will attempt to load DICOM data dictionaries specified The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. .SH "COPYRIGHT" .PP -Copyright (C) 1998-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1998-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmpsprt.1 b/doxygen/manpages/man1/dcmpsprt.1 index 65712dec..fe4364aa 100644 --- a/doxygen/manpages/man1/dcmpsprt.1 +++ b/doxygen/manpages/man1/dcmpsprt.1 @@ -1,4 +1,4 @@ -.TH "dcmpsprt" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmpsprt" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmpsprt \- Read DICOM images and presentation states and render print job @@ -274,4 +274,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmprscu\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1999-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1999-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmpsrcv.1 b/doxygen/manpages/man1/dcmpsrcv.1 index f6d84fa0..db9c798e 100644 --- a/doxygen/manpages/man1/dcmpsrcv.1 +++ b/doxygen/manpages/man1/dcmpsrcv.1 @@ -1,4 +1,4 @@ -.TH "dcmpsrcv" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmpsrcv" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmpsrcv \- Network receive for presentation state viewer @@ -79,4 +79,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmpssnd\fP(1), \fBstorescp\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1998-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1998-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmpssnd.1 b/doxygen/manpages/man1/dcmpssnd.1 index 162b5a77..5cd14a4e 100644 --- a/doxygen/manpages/man1/dcmpssnd.1 +++ b/doxygen/manpages/man1/dcmpssnd.1 @@ -1,4 +1,4 @@ -.TH "dcmpssnd" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmpssnd" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmpssnd \- Network send for presentation state viewer @@ -84,4 +84,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmpsrcv\fP(1), \fBstorescu\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1998-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1998-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmqridx.1 b/doxygen/manpages/man1/dcmqridx.1 index f33f1ff7..a1abbcd6 100644 --- a/doxygen/manpages/man1/dcmqridx.1 +++ b/doxygen/manpages/man1/dcmqridx.1 @@ -1,4 +1,4 @@ -.TH "dcmqridx" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmqridx" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmqridx \- Register a DICOM image file in an image database index file @@ -85,4 +85,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmqrscp\fP(1), \fBdcmqrti\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1993-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1993-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmqrscp.1 b/doxygen/manpages/man1/dcmqrscp.1 index 3e2ff982..3ba26be9 100644 --- a/doxygen/manpages/man1/dcmqrscp.1 +++ b/doxygen/manpages/man1/dcmqrscp.1 @@ -1,4 +1,4 @@ -.TH "dcmqrscp" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmqrscp" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmqrscp \- DICOM image archive (central test node) @@ -172,6 +172,17 @@ restriction of query/retrieve models: .SS "network options" .PP .nf +IP protocol version: + + -i4 --ipv4 + use IPv4 only (default) + + -i6 --ipv6 + use IPv6 only + + -i0 --ip-auto + use IPv6/IPv4 dual stack + association negotiation profiles from configuration file: -xf --assoc-config-file @@ -865,6 +876,8 @@ ElectromyogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7 ElectrooculogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7.3 SleepElectroencephalogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7.4 BodyPositionWaveformStorage 1.2.840.10008.5.1.4.1.1.9.8.1 +WaveformPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.1 +WaveformAcquisitionPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.2 RETIRED_StandaloneModalityLUTStorage 1.2.840.10008.5.1.4.1.1.10 RETIRED_StandaloneVOILUTStorage 1.2.840.10008.5.1.4.1.1.11 GrayscaleSoftcopyPresentationStateStorage 1.2.840.10008.5.1.4.1.1.11.1 @@ -1012,6 +1025,7 @@ DICONDE_EddyCurrentImageStorage 1.2.840.10008.5.1.4.1.1.601 DICONDE_EddyCurrentMultiframeImageStorage 1.2.840.10008.5.1.4.1.1.601.2 DICONDE_ThermographyImageStorage 1.2.840.10008.5.1.4.1.1.601.3 DICONDE_ThermographyMultiFrameImageStorage 1.2.840.10008.5.1.4.1.1.601.4 +DICONDE_UltrasoundWaveformStorage 1.2.840.10008.5.1.4.1.1.601.5 DRAFT_RTBeamsDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.1 RTBeamsDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.7 RTBrachyApplicationSetupDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.10 @@ -1076,7 +1090,7 @@ Query/Retrieve Level: PATIENT (or STUDY for the Study Root Q/R model) (0010,0040) PatientSex (0010,1000) OtherPatientIDs (retired) (0010,1001) OtherPatientNames -(0010,2160) EthnicGroup +(0010,2160) EthnicGroup (retired) (0010,4000) PatientComments .fi .PP @@ -1099,7 +1113,7 @@ Query/Retrieve Level: STUDY (0010,21B0) AdditionalPatientHistory (0020,000D) StudyInstanceUID (0020,0010) StudyID -(0020,1070) RETIRED_OtherStudyNumbers +(0020,1070) OtherStudyNumbers (retired) .fi .PP .PP @@ -1160,4 +1174,4 @@ The mapping table files are expected in DCMTK's \fI\fP\&. The \fIDCMICO \fBdcmqridx\fP(1), \fBdcmqrti\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1993-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1993-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmqrti.1 b/doxygen/manpages/man1/dcmqrti.1 index b9d8c55e..423c55ef 100644 --- a/doxygen/manpages/man1/dcmqrti.1 +++ b/doxygen/manpages/man1/dcmqrti.1 @@ -1,4 +1,4 @@ -.TH "dcmqrti" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmqrti" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmqrti \- The Terminal Initiator Telnet Client Program @@ -324,4 +324,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmqrscp\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1993-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1993-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmquant.1 b/doxygen/manpages/man1/dcmquant.1 index d0838d10..5f2e8892 100644 --- a/doxygen/manpages/man1/dcmquant.1 +++ b/doxygen/manpages/man1/dcmquant.1 @@ -1,4 +1,4 @@ -.TH "dcmquant" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmquant" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmquant \- Convert DICOM color images to palette color @@ -243,4 +243,4 @@ The \fBdcmquant\fP utility will attempt to load DICOM data dictionaries specifie The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. .SH "COPYRIGHT" .PP -Copyright (C) 2001-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2001-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmrecv.1 b/doxygen/manpages/man1/dcmrecv.1 index 41d89d2e..5198d3ef 100644 --- a/doxygen/manpages/man1/dcmrecv.1 +++ b/doxygen/manpages/man1/dcmrecv.1 @@ -1,4 +1,4 @@ -.TH "dcmrecv" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmrecv" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmrecv \- Simple DICOM storage SCP (receiver) @@ -57,6 +57,17 @@ port tcp/ip port number to listen on .SS "network options" .PP .nf +IP protocol version: + + -i4 --ipv4 + use IPv4 only (default) + + -i6 --ipv6 + use IPv6 only + + -i0 --ip-auto + use IPv6/IPv4 dual stack + association negotiation profile from configuration file: -xf --config-file [f]ilename, [p]rofile: string @@ -409,4 +420,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmsend\fP(1), \fBstorescu\fP(1), \fBstorescp\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2013-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2013-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmscale.1 b/doxygen/manpages/man1/dcmscale.1 index 68bd6040..3b8c383c 100644 --- a/doxygen/manpages/man1/dcmscale.1 +++ b/doxygen/manpages/man1/dcmscale.1 @@ -1,4 +1,4 @@ -.TH "dcmscale" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmscale" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmscale \- Scale DICOM images @@ -229,4 +229,4 @@ The \fBdcmscale\fP utility will attempt to load DICOM data dictionaries specifie The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. .SH "COPYRIGHT" .PP -Copyright (C) 2002-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2002-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmsend.1 b/doxygen/manpages/man1/dcmsend.1 index 7a366b04..6866e924 100644 --- a/doxygen/manpages/man1/dcmsend.1 +++ b/doxygen/manpages/man1/dcmsend.1 @@ -1,4 +1,4 @@ -.TH "dcmsend" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmsend" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmsend \- Simple DICOM storage SCU (sender) @@ -328,4 +328,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmrecv\fP(1), \fBstorescu\fP(1), \fBstorescp\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2011-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2011-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcmsign.1 b/doxygen/manpages/man1/dcmsign.1 index b60dfed1..3e1d382e 100644 --- a/doxygen/manpages/man1/dcmsign.1 +++ b/doxygen/manpages/man1/dcmsign.1 @@ -1,4 +1,4 @@ -.TH "dcmsign" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcmsign" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcmsign \- Sign and Verify DICOM Files @@ -496,4 +496,4 @@ The \fBdcmsign\fP utility will attempt to load DICOM data dictionaries specified The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. .SH "COPYRIGHT" .PP -Copyright (C) 2000-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2000-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dcod2lum.1 b/doxygen/manpages/man1/dcod2lum.1 index 64d3b9db..50ea71ae 100644 --- a/doxygen/manpages/man1/dcod2lum.1 +++ b/doxygen/manpages/man1/dcod2lum.1 @@ -1,4 +1,4 @@ -.TH "dcod2lum" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dcod2lum" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dcod2lum \- Convert hardcopy characteristic curve file to softcopy format @@ -47,4 +47,4 @@ The format of both input and output file is described the documentation of the \ \fBdcmdspfn\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2002-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2002-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dconvlum.1 b/doxygen/manpages/man1/dconvlum.1 index 618e50e3..677511bc 100644 --- a/doxygen/manpages/man1/dconvlum.1 +++ b/doxygen/manpages/man1/dconvlum.1 @@ -1,4 +1,4 @@ -.TH "dconvlum" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dconvlum" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dconvlum \- Convert VeriLUM files to DCMTK display files @@ -34,4 +34,4 @@ See DICOM standard part 14 for more details on display calibration and Barten's \fBdcmdspfn\fP(1), \fBdcm2pnm\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1999-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1999-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/drtdump.1 b/doxygen/manpages/man1/drtdump.1 index 44acad90..2ee6ad06 100644 --- a/doxygen/manpages/man1/drtdump.1 +++ b/doxygen/manpages/man1/drtdump.1 @@ -1,4 +1,4 @@ -.TH "drtdump" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "drtdump" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME drtdump \- Dump DICOM RT file and data set @@ -132,4 +132,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmconv\fP(1), \fBdcmdump\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2010-2024 by OFFIS e\&.V\&. and ICSMED AG, Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2010-2025 by OFFIS e\&.V\&. and ICSMED AG, Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dsr2html.1 b/doxygen/manpages/man1/dsr2html.1 index ac984eb0..84bbc742 100644 --- a/doxygen/manpages/man1/dsr2html.1 +++ b/doxygen/manpages/man1/dsr2html.1 @@ -1,4 +1,4 @@ -.TH "dsr2html" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dsr2html" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dsr2html \- Render DICOM SR file and data set to HTML/XHTML @@ -250,8 +250,12 @@ PatientRadiationDoseSRStorage 1.2.840.10008.5.1.4.1.1.88.73 PlannedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.74 PerformedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.75 WaveformAnnotationSRStorage 1.2.840.10008.5.1.4.1.1.88.77 + +RenditionSelectionDocumentRealTimeCommunication 1.2.840.10008.10.4 (*) .fi .PP +.PP +(*) This is not a Storage SOP Class, but used for Real-Time Communication\&. .SS "Character Encoding" The HTML/XHTML encoding is determined automatically from the DICOM attribute (0008,0005) 'Specific Character Set' using the following mapping: .PP @@ -281,6 +285,8 @@ Chinese "GBK" => "GBK" If this DICOM attribute is missing in the input file, although needed, option \fI--charset-assume\fP can be used to specify an appropriate character set manually (using one of the DICOM defined terms)\&. For reasons of backward compatibility with previous versions of this tool, the following terms are also supported and mapped automatically to the associated DICOM defined terms: latin-1, latin-2, latin-3, latin-4, latin-5, latin-9, cyrillic, arabic, greek, hebrew\&. .PP Option \fI--convert-to-utf8\fP can be used to convert the DICOM file or data set to UTF-8 encoding prior to the rendering to HTML/XHTML format\&. +.SS "Security" +Please note that using one of the options \fI--css-reference\fP, \fI--css-file\fP or \fI--hyperlink-url-prefix\fP can lead to security issues, as an attacker could misuse them to potentially inject dangerous content into the HTML/XHTML output\&. The values passed to these options are not checked, neither the URL and prefix nor the content of the specified CSS file\&. .SH "LOGGING" .PP The level of logging output of the various command line tools and underlying libraries can be specified by the user\&. By default, only errors and warnings are written to the standard error stream\&. Using option \fI--verbose\fP also informational messages like processing details are reported\&. Option \fI--debug\fP can be used to get more details on the internal activity, e\&.g\&. for debugging purposes\&. Other logging levels can be selected using option \fI--log-level\fP\&. In \fI--quiet\fP mode only fatal errors are reported\&. In such very severe error events, the application will usually terminate\&. For more details on the different logging levels, see documentation of module 'oflog'\&. @@ -304,10 +310,12 @@ Depending on the command line options specified, the \fBdsr2html\fP utility will The mapping table files are expected in DCMTK's \fI\fP\&. The \fIDCMICONVPATH\fP environment variable can be used to specify a different location\&. If a different location is specified, those mapping tables also replace any built-in tables\&. .SH "FILES" .PP -\fI/report\&.css\fP - Sample Cascading Stylesheet file for HTML \fI/reportx\&.css\fP - Sample Cascading Stylesheet file for XHTML +\fI/report\&.css\fP - Sample Cascading Stylesheet file for HTML +.br +\fI/reportx\&.css\fP - Sample Cascading Stylesheet file for XHTML .SH "SEE ALSO" .PP \fBdcmconv\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2000-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2000-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dsr2xml.1 b/doxygen/manpages/man1/dsr2xml.1 index d3b43f49..7eaa8dda 100644 --- a/doxygen/manpages/man1/dsr2xml.1 +++ b/doxygen/manpages/man1/dsr2xml.1 @@ -1,4 +1,4 @@ -.TH "dsr2xml" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dsr2xml" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dsr2xml \- Convert DICOM SR file and data set to XML @@ -211,9 +211,13 @@ PatientRadiationDoseSRStorage 1.2.840.10008.5.1.4.1.1.88.73 PlannedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.74 PerformedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.75 WaveformAnnotationSRStorage 1.2.840.10008.5.1.4.1.1.88.77 + +RenditionSelectionDocumentRealTimeCommunication 1.2.840.10008.10.4 (*) .fi .PP .PP +(*) This is not a Storage SOP Class, but used for Real-Time Communication\&. +.PP Please note that currently only mandatory and some optional attributes are supported\&. .SS "Character Encoding" The XML encoding is determined automatically from the DICOM attribute (0008,0005) 'Specific Character Set' using the following mapping: @@ -279,4 +283,4 @@ The mapping table files are expected in DCMTK's \fI\fP\&. The \fIDCMICO \fBxml2dsr\fP(1), \fBdcmconv\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2000-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2000-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dsrdump.1 b/doxygen/manpages/man1/dsrdump.1 index 10ced1a8..c21a6143 100644 --- a/doxygen/manpages/man1/dsrdump.1 +++ b/doxygen/manpages/man1/dsrdump.1 @@ -1,4 +1,4 @@ -.TH "dsrdump" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dsrdump" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dsrdump \- Dump DICOM SR file and data set @@ -219,8 +219,12 @@ PatientRadiationDoseSRStorage 1.2.840.10008.5.1.4.1.1.88.73 PlannedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.74 PerformedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.75 WaveformAnnotationSRStorage 1.2.840.10008.5.1.4.1.1.88.77 + +RenditionSelectionDocumentRealTimeCommunication 1.2.840.10008.10.4 (*) .fi .PP +.PP +(*) This is not a Storage SOP Class, but used for Real-Time Communication\&. .SH "LOGGING" .PP The level of logging output of the various command line tools and underlying libraries can be specified by the user\&. By default, only errors and warnings are written to the standard error stream\&. Using option \fI--verbose\fP also informational messages like processing details are reported\&. Option \fI--debug\fP can be used to get more details on the internal activity, e\&.g\&. for debugging purposes\&. Other logging levels can be selected using option \fI--log-level\fP\&. In \fI--quiet\fP mode only fatal errors are reported\&. In such very severe error events, the application will usually terminate\&. For more details on the different logging levels, see documentation of module 'oflog'\&. @@ -247,4 +251,4 @@ The mapping table files are expected in DCMTK's \fI\fP\&. The \fIDCMICO \fBdcmconv\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2000-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2000-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/dump2dcm.1 b/doxygen/manpages/man1/dump2dcm.1 index 60972d4c..0940e0b7 100644 --- a/doxygen/manpages/man1/dump2dcm.1 +++ b/doxygen/manpages/man1/dump2dcm.1 @@ -1,4 +1,4 @@ -.TH "dump2dcm" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "dump2dcm" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME dump2dcm \- Convert ASCII dump to DICOM file @@ -265,4 +265,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcmdump\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1996-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1996-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/echoscu.1 b/doxygen/manpages/man1/echoscu.1 index 0693752c..cdcd9ed6 100644 --- a/doxygen/manpages/man1/echoscu.1 +++ b/doxygen/manpages/man1/echoscu.1 @@ -1,4 +1,4 @@ -.TH "echoscu" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "echoscu" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME echoscu \- DICOM verification (C-ECHO) SCU @@ -77,7 +77,7 @@ application entity titles: association negotiation debugging: - -pts --propose-ts [n]umber: integer (1..52) + -pts --propose-ts [n]umber: integer (1..53) propose n transfer syntaxes -ppc --propose-pc [n]umber: integer (1..128) @@ -366,4 +366,4 @@ The \fBechoscu\fP utility will attempt to load DICOM data dictionaries specified The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. .SH "COPYRIGHT" .PP -Copyright (C) 1994-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1994-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/findscu.1 b/doxygen/manpages/man1/findscu.1 index 14a5dacd..55c88c76 100644 --- a/doxygen/manpages/man1/findscu.1 +++ b/doxygen/manpages/man1/findscu.1 @@ -1,4 +1,4 @@ -.TH "findscu" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "findscu" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME findscu \- DICOM query (C-FIND) SCU @@ -425,4 +425,4 @@ The mapping table files are expected in DCMTK's \fI\fP\&. The \fIDCMICO \fBmovescu\fP(1), \fBdump2dcm\fP(1), \fBdcmodify\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1994-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1994-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/getscu.1 b/doxygen/manpages/man1/getscu.1 index 0b501ee5..6f84657b 100644 --- a/doxygen/manpages/man1/getscu.1 +++ b/doxygen/manpages/man1/getscu.1 @@ -1,4 +1,4 @@ -.TH "getscu" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "getscu" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME getscu \- DICOM retrieve (C-GET) SCU @@ -449,6 +449,8 @@ BasicVoiceAudioWaveformStorage 1.2.840.10008.5.1.4.1.1.9.4 GeneralAudioWaveformStorage 1.2.840.10008.5.1.4.1.1.9.4.2 ArterialPulseWaveformStorage 1.2.840.10008.5.1.4.1.1.9.5.1 RespiratoryWaveformStorage 1.2.840.10008.5.1.4.1.1.9.6.1 +WaveformPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.1 +WaveformAcquisitionPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.2 RETIRED_StandaloneModalityLUTStorage 1.2.840.10008.5.1.4.1.1.10 RETIRED_StandaloneVOILUTStorage 1.2.840.10008.5.1.4.1.1.11 GrayscaleSoftcopyPresentationStateStorage 1.2.840.10008.5.1.4.1.1.11.1 @@ -606,4 +608,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBfindscu\fP(1), \fBmovescu\fP(1), \fBdump2dcm\fP(1), \fBdcmodify\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2011-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2011-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/img2dcm.1 b/doxygen/manpages/man1/img2dcm.1 index b92286cf..a59239ed 100644 --- a/doxygen/manpages/man1/img2dcm.1 +++ b/doxygen/manpages/man1/img2dcm.1 @@ -1,4 +1,4 @@ -.TH "img2dcm" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "img2dcm" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME img2dcm \- Convert standard image formats into DICOM format @@ -314,7 +314,7 @@ Color and grayscale images are supported\&. CP-1843 enforce that the value of Pl .PP For DICOM it is clear that SPIFF header should not be present in the DICOM object's internal JPEG-LS stream\&. The plugin will simply rejects any input JPEG-LS file containing a SPIFF header at marker APP8\&. .PP -By default, all APPn markers are cut off from the original JPEG-LS stream\&. However, if you want to keep APPn markers (e\&.g\&. APP8/HP color transform information, aka 'mrfx') inside the DICOM stream, the option \fI--keep-appn\fP does the trick\&. Pay attention that the plugin will check the actual color transform specified in the APP8/HP marker\&. Since DICOM does not allow any color transform to be specified in the APP8 marker, only a value of \fC0\fP (no color transform) is accepted\&. +By default, all APPn markers are cut off from the original JPEG-LS stream\&. However, if you want to keep APPn markers (e\&.g\&. APP8/HP color transform information, aka 'mrfx') inside the DICOM stream, the option \fI--keep-appn\fP does the trick\&. Pay attention that the plugin will check the actual color transform specified in the APP8/HP marker\&. Since DICOM does not allow any color transform to be specified in the APP8 marker, only a value of 0 (no color transform) is accepted\&. .SS "BMP Input Plugin" \fBimg2dcm\fP supports BMP as input format\&. However, so far only the most common BMP images are supported\&. In particular, BMP images which use bit fields or run length encoding will be rejected\&. Such images are uncommon\&. Input images will either be converted into a DICOM image with RGB color model and a bit depth of 24, or into an image with MONOCHROME2 color model an 8 bits per pixel\&. There are no specific options for fine-tuning BMP format conversion\&. .SS "Output Plugins" @@ -387,4 +387,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcm2pnm\fP(1), \fBdcmj2pnm\fP(1), \fBdump2dcm\fP(1), \fBdcmconv\fP(1), \fBdcmodify\fP(1), \fBdcm2xml\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2007-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2007-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/json2dcm.1 b/doxygen/manpages/man1/json2dcm.1 new file mode 100644 index 00000000..128d3e06 --- /dev/null +++ b/doxygen/manpages/man1/json2dcm.1 @@ -0,0 +1,398 @@ +.TH "json2dcm" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- +.nh +.SH NAME +json2dcm \- Convert JSON document to DICOM file or data set + +.SH "SYNOPSIS" +.PP +.PP +.nf +json2dcm [options] jsonfile-in dcmfile-out +.fi +.PP +.SH "DESCRIPTION" +.PP +The \fBjson2dcm\fP utility converts the contents of a JSON (JavaScript Object Notation) document to a binary DICOM file or data set\&. The JSON document is expected to conform to the 'DICOM JSON Model' as defined in DICOM Part 18 Section F\&. Such JSON files can be created, e\&.g\&., using the \fBdcm2json\fP tool\&. +.SH "PARAMETERS" +.PP +.PP +.nf +jsonfile-in JSON input filename to be converted ("-" for stdin) + +dcmfile-out DICOM output filename ("-" for stdout) +.fi +.PP +.SH "OPTIONS" +.PP +.SS "general options" +.PP +.nf + -h --help + print this help text and exit + + --version + print version information and exit + + --arguments + print expanded command line arguments + + -q --quiet + quiet mode, print no warnings and errors + + -v --verbose + verbose mode, print processing details + + -d --debug + debug mode, print debug information + + -ll --log-level [l]evel: string constant + (fatal, error, warn, info, debug, trace) + use level l for the logger + + -lc --log-config [f]ilename: string + use config file f for the logger +.fi +.PP +.SS "input options" +.PP +.nf +input file format: + + +f --read-meta-info + read meta information if present (default) + + -f --ignore-meta-info + ignore file meta information +.fi +.PP +.SS "processing options" +.PP +.nf +unique identifiers: + + +Ug --generate-new-uids + generate new Study/Series/SOP Instance UID + + -Uo --dont-overwrite-uids + do not overwrite existing UIDs (default) + + +Uo --overwrite-uids + overwrite existing UIDs + +bulkdata URI handling: + + +Bu --parse-bulkdata-uri + parse Bulkdata URIs (default) + + -Bu --ignore-bulkdata-uri + ignore Bulkdata URIs + + +Bd --add-bulkdata-dir [d]irectory: string + add d to list of permitted bulk data sources + +handling of arrays with multiple data sets: + + -ar --array-reject + reject multiple data sets (default) + + +as --array-select [n]umber: integer + select data set n from array + + +ar --array-sequence + store all data sets in private sequence +.fi +.PP +.SS "output options" +.PP +.nf +output file format: + + +F --write-file + write file format (default) + + -F --write-dataset + write data set without file meta information + + +Fu --update-meta-info + update particular file meta information + +output transfer syntax: + + +t= --write-xfer-same + write with same TS as input (default) + + +te --write-xfer-little + write with explicit VR little endian TS + + +tb --write-xfer-big + write with explicit VR big endian TS + + +ti --write-xfer-implicit + write with implicit VR little endian TS + + +td --write-xfer-deflated + write with deflated explicit VR little endian TS + +error handling: + + -E --stop-on-error + do not write if document is invalid (default) + + +E --ignore-errors + attempt to write even if document is invalid + +post-1993 value representations: + + +u --enable-new-vr + enable support for new VRs (UN/UT) (default) + + -u --disable-new-vr + disable support for new VRs, convert to OB + +length encoding in sequences and items: + + +e --length-explicit + write with explicit lengths (default) + + -e --length-undefined + write with undefined lengths + +charset handling: + + +c --charset-accept + write with the given charset in JSON (default) + + -c --charset-replace + replace the given charset in JSON with UTF-8 + +deflate compression level (only with --write-xfer-deflated): + + +cl --compression-level [l]evel: integer (default: 6) + 0=uncompressed, 1=fastest, 9=best compression +.fi +.PP +.SH "NOTES" +.PP +The basic structure of the JSON input expected looks like the following (see DICOM Part 18 Section F for details): +.PP +.PP +.nf +{ + "00080005": { + "vr": "CS", + "Value": [ + "ISO_IR 192" + ] + }, + "00080020": { + "vr": "DT", + "Value": [ + "20130409" + ] + }, + "00080030": { + "vr": "TM", + "Value": [ + "131600.0000" + ] + }, + "00080050": { + "vr": "SH", + "Value": [ + "11235813" + ] + }, + "00080056": { + "vr": "CS", + "Value": [ + "ONLINE" + ] + }, + "00080061": { + "vr": "CS", + "Value": [ + "CT", + "PET" + ] + }, + "00080090": { + "vr": "PN", + "Value": [ + { + "Alphabetic": "^Bob^^Dr." + } + ] + }, + "00081190": { + "vr": "UR", + "Value": [ + "http://wado.nema.org/studies/ + 1.2.392.200036.9116.2.2.2.1762893313.1029997326.945873" + ] + }, + "00090010": { + "vr": "LO", + "Value": [ + "Vendor A" + ] + }, + "00091002": { + "vr": "UN", + "InlineBinary": "z0x9c8v7" + }, + "00100010": { + "vr": "PN", + "Value": [ + { + "Alphabetic": "Wang^XiaoDong" + } + ] + }, + "00100020": { + "vr": "LO", + "Value": [ + "12345" + ] + }, + "00100021": { + "vr": "LO", + "Value": [ + "Hospital A" + ] + }, + "00100030": { + "vr": "DA", + "Value": [ + "19670701" + ] + }, + "00100040": { + "vr": "CS", + "Value": [ + "M" + ] + }, + "00101002": { + "vr": "SQ", + "Value": [ + { + "00100020": { + "vr": "LO", + "Value": [ + "54321" + ] + }, + "00100021": { + "vr": "LO", + "Value": [ + "Hospital B" + ] + } + }, + { + "00100020": { + "vr": "LO", + "Value": [ + "24680" + ] + }, + "00100021": { + "vr": "LO", + "Value": [ + "Hospital C" + ] + } + } + ] + }, + "0020000D": { + "vr": "UI", + "Value": [ + "1.2.392.200036.9116.2.2.2.1762893313.1029997326.945873" + ] + }, + "00200010": { + "vr": "SH", + "Value": [ + "11235813" + ] + }, + "00201206": { + "vr": "IS", + "Value": [ + 4 + ] + }, + "00201208": { + "vr": "IS", + "Value": [ + 942 + ] + } +} +.fi +.PP +.SS "Character Encoding" +The JSON format only supports UTF-8 encoding\&. Thus the generated DICOM file will also contain UTF-8 encoding\&. If the JSON file does not contain a specific character set, or a specific character set other than 'ISO_IR 192', a warning will be issued\&. +.SS "Binary Data, Bulk Data, and Pixel Data" +The DICOM JSON Model uses 'InlineBinary' to store attribute values of binary value representations such as 'OB', 'OW', 'OD', 'OF', 'OL', 'OV' etc\&. in Base64 encoded form\&. This is supported in \fBjson2dcm\fP for all binary attributes, including unencapsulated pixel data\&. +.PP +The DICOM JSON Model also permits attribute values to be stored separately from the JSON data set and to be referenced through a BulkDataURI\&. This is supported for file URIs that reference files in the local filesystem\&. \fBjson2dcm\fP tool also supports the inofficial extension to the file URI scheme generated by \fIDCM4CHE\fP, where parameters named 'offset' and 'length' are appended to the file URI in order to refer to a specific part of a file\&. HTTP URIs as well as URIs that identify another part in a MIME multipart/related structure are not yet supported in \fBjson2dcm\fP\&. If the command line option \fI--ignore-bulkdata-uri\fP is specified, then all bulk data URIs are ignored and attributes with bulk data will be written with empty value\&. +.PP +Finally, encapsulated (in particular, compressed) pixel data is not supported by \fBjson2dcm\fP because the syntax of the DICOM JSON Model for this specific case is not defined in the DICOM standard yet\&. +.SS "Arrays of Data Sets" +The DICOM JSON Model uses a JSON array structure to return multiple data sets in DICOMweb services such as WADO-RS or QIDO-RS\&. JSON arrays containing a single DICOM data set are automatically recognized by \fBjson2dcm\fP and treated like a data set without the surrounding array structure\&. JSON arrays containing multiple data sets are rejected by default\&. Alternatively, the \fI--array-select\fP option can be used to select one data set from the array to be converted\&. The \fI--array-sequence\fP option causes all data sets to be written as sequence items into a single private sequence with attribute tag (0009,1000)\&. Such files, which are mostly intended for debugging purposes, can be recognized by the private creator element (0009,0010), which has the value 'JSON2DCM_LIST_OF_DATASETS'\&. +.SS "Trailing Commas" +Trailing commas are not permitted in JSON, but \fBjson2dcm\fP will still accept such JSON data sets without warning or error message because they are handled gracefully by the underlying JSON parser\&. Users should, therefore, not assume that a JSON data set is valid just because \fBjson2dcm\fP accepts it\&. This tool is not designed as a validator for JSON or the DICOM JSON Model\&. +.SH "LOGGING" +.PP +The level of logging output of the various command line tools and underlying libraries can be specified by the user\&. By default, only errors and warnings are written to the standard error stream\&. Using option \fI--verbose\fP also informational messages like processing details are reported\&. Option \fI--debug\fP can be used to get more details on the internal activity, e\&.g\&. for debugging purposes\&. Other logging levels can be selected using option \fI--log-level\fP\&. In \fI--quiet\fP mode only fatal errors are reported\&. In such very severe error events, the application will usually terminate\&. For more details on the different logging levels, see documentation of module 'oflog'\&. +.PP +In case the logging output should be written to file (optionally with logfile rotation), to syslog (Unix) or the event log (Windows) option \fI--log-config\fP can be used\&. This configuration file also allows for directing only certain messages to a particular output stream and for filtering certain messages based on the module or application where they are generated\&. An example configuration file is provided in \fI/logger\&.cfg\fP\&. +.SH "COMMAND LINE" +.PP +All command line tools use the following notation for parameters: square brackets enclose optional values (0-1), three trailing dots indicate that multiple values are allowed (1-n), a combination of both means 0 to n values\&. +.PP +Command line options are distinguished from parameters by a leading '+' or '-' sign, respectively\&. Usually, order and position of command line options are arbitrary (i\&.e\&. they can appear anywhere)\&. However, if options are mutually exclusive the rightmost appearance is used\&. This behavior conforms to the standard evaluation rules of common Unix shells\&. +.PP +In addition, one or more command files can be specified using an '@' sign as a prefix to the filename (e\&.g\&. \fI@command\&.txt\fP)\&. Such a command argument is replaced by the content of the corresponding text file (multiple whitespaces are treated as a single separator unless they appear between two quotation marks) prior to any further evaluation\&. Please note that a command file cannot contain another command file\&. This simple but effective approach allows one to summarize common combinations of options/parameters and avoids longish and confusing command lines (an example is provided in file \fI/dumppat\&.txt\fP)\&. +.SH "EXIT CODES" +.PP +The \fBdcm2json\fP utility uses the following exit codes when terminating\&. This enables the user to check for the reason why the application terminated\&. +.SS "general" +.PP +.nf +EXITCODE_NO_ERROR 0 +EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 +.fi +.PP +.SS "input file errors" +.PP +.nf +EXITCODE_CANNOT_READ_INPUT_FILE 20 +.fi +.PP +.SS "output file errors" +.PP +.nf +EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 +.fi +.PP +.SS "processing errors" +.PP +.nf +EXITCODE_INVALID_JSON_CONTENT 65 +EXITCODE_BULKDATA_URI_NOT_SUPPORTED 66 +.fi +.PP +.SH "ENVIRONMENT" +.PP +The \fBjson2dcm\fP utility will attempt to load DICOM data dictionaries specified in the \fIDCMDICTPATH\fP environment variable\&. By default, i\&.e\&. if the \fIDCMDICTPATH\fP environment variable is not set, the file \fI/dicom\&.dic\fP will be loaded unless the dictionary is built into the application (default for Windows)\&. +.PP +The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. +.SH "SEE ALSO" +.PP +\fBdcm2json\fP(1) \fBdump2dcm\fP(2) +.SH "COPYRIGHT" +.PP +Copyright (C) 2024-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/mkcsmapper.1 b/doxygen/manpages/man1/mkcsmapper.1 index 39de1458..976740f6 100644 --- a/doxygen/manpages/man1/mkcsmapper.1 +++ b/doxygen/manpages/man1/mkcsmapper.1 @@ -1,4 +1,4 @@ -.TH "mkcsmapper" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "mkcsmapper" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME mkcsmapper \- Create csmapper conversion files for oficonv diff --git a/doxygen/manpages/man1/mkesdb.1 b/doxygen/manpages/man1/mkesdb.1 index f6223e1d..45bf1b6f 100644 --- a/doxygen/manpages/man1/mkesdb.1 +++ b/doxygen/manpages/man1/mkesdb.1 @@ -1,4 +1,4 @@ -.TH "mkesdb" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "mkesdb" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME mkesdb \- Create encoding scheme database (esdb) files for oficonv diff --git a/doxygen/manpages/man1/movescu.1 b/doxygen/manpages/man1/movescu.1 index cedf4e26..f6dc7237 100644 --- a/doxygen/manpages/man1/movescu.1 +++ b/doxygen/manpages/man1/movescu.1 @@ -1,4 +1,4 @@ -.TH "movescu" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "movescu" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME movescu \- DICOM retrieve (C-MOVE) SCU @@ -58,6 +58,17 @@ dcmfile-in DICOM query file(s) .SS "network options" .PP .nf +IP protocol version: + + -i4 --ipv4 + use IPv4 only (default) + + -i6 --ipv6 + use IPv6 only + + -i0 --ip-auto + use IPv6/IPv4 dual stack + override matching keys: -k --key [k]ey: gggg,eeee="str" or dictionary name="str" @@ -232,6 +243,131 @@ other network options: silently correct space-padded UIDs .fi .PP +.SS "transport layer security (TLS) options" +.PP +.nf +transport protocol stack: + + -tls --disable-tls + use normal TCP/IP connection (default) + + +tls --enable-tls [p]rivate key file, [c]ertificate file: string + use authenticated secure TLS connection + +private key password (only with --enable-tls): + + +ps --std-passwd + prompt user to type password on stdin (default) + + +pw --use-passwd [p]assword: string + use specified password + + -pw --null-passwd + use empty string as password + +key and certificate file format: + + -pem --pem-keys + read keys and certificates as PEM file (default) + + -der --der-keys + read keys and certificates as DER file + +certification authority: + + +cf --add-cert-file [f]ilename: string + add certificate file to list of certificates + + +cd --add-cert-dir [d]irectory: string + add certificates in d to list of certificates + + +crl --add-crl-file [f]ilename: string + add certificate revocation list file + (implies --enable-crl-vfy) + + +crv --enable-crl-vfy + enable leaf CRL verification + + +cra --enable-crl-all + enable full chain CRL verification + +security profile: + + +ph --list-profiles + list supported TLS profiles and exit + + +pg --profile-8996 + BCP 195 RFC 8996 TLS Profile (default) + + +pm --profile-8996-mod + Modified BCP 195 RFC 8996 TLS Profile + + # only available if underlying TLS library supports + # all TLS features required for this profile + + +py --profile-bcp195-nd + Non-downgrading BCP 195 TLS Profile (retired) + + +px --profile-bcp195 + BCP 195 TLS Profile (retired) + + +pz --profile-bcp195-ex + Extended BCP 195 TLS Profile (retired) + + +pb --profile-basic + Basic TLS Secure Transport Connection Profile (retired) + + # only available if underlying TLS library supports 3DES + + +pa --profile-aes + AES TLS Secure Transport Connection Profile (retired) + + +pn --profile-null + Authenticated unencrypted communication + (retired, was used in IHE ATNA) + +ciphersuite: + + +cc --list-ciphers + list supported TLS ciphersuites and exit + + +cs --cipher [c]iphersuite name: string + add ciphersuite to list of negotiated suites + + +dp --dhparam [f]ilename: string + read DH parameters for DH/DSS ciphersuites + +server name indication: + + --no-sni + do not use SNI (default) + + --expect-sni [s]erver name: string + expect requests for server name s + +pseudo random generator: + + +rs --seed [f]ilename: string + seed random generator with contents of f + + +ws --write-seed + write back modified seed (only with --seed) + + +wf --write-seed-file [f]ilename: string (only with --seed) + write modified seed to file f + +peer authentication: + + -rc --require-peer-cert + verify peer certificate, fail if absent (default) + + -vc --verify-peer-cert + verify peer certificate if present + + -ic --ignore-peer-cert + don't verify peer certificate +.fi +.PP .SS "output options" .PP .nf @@ -444,6 +580,8 @@ ElectromyogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7 ElectrooculogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7.3 SleepElectroencephalogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7.4 BodyPositionWaveformStorage 1.2.840.10008.5.1.4.1.1.9.8.1 +WaveformPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.1 +WaveformAcquisitionPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.2 RETIRED_StandaloneModalityLUTStorage 1.2.840.10008.5.1.4.1.1.10 RETIRED_StandaloneVOILUTStorage 1.2.840.10008.5.1.4.1.1.11 GrayscaleSoftcopyPresentationStateStorage 1.2.840.10008.5.1.4.1.1.11.1 @@ -591,6 +729,7 @@ DICONDE_EddyCurrentImageStorage 1.2.840.10008.5.1.4.1.1.601 DICONDE_EddyCurrentMultiframeImageStorage 1.2.840.10008.5.1.4.1.1.601.2 DICONDE_ThermographyImageStorage 1.2.840.10008.5.1.4.1.1.601.3 DICONDE_ThermographyMultiFrameImageStorage 1.2.840.10008.5.1.4.1.1.601.4 +DICONDE_UltrasoundWaveformStorage 1.2.840.10008.5.1.4.1.1.601.5 DRAFT_RTBeamsDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.1 RTBeamsDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.7 RTBrachyApplicationSetupDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.10 @@ -646,6 +785,7 @@ HighThroughputJPEG2000ImageCompressionLossless.Tr.S. 1.2.840.10008.1.2.4.201 HighThroughputJPEG2000RPCLImageCompressionLoss.Tr.S. 1.2.840.10008.1.2.4.202 HighThroughputJPEG2000ImageCompressionTransferSynta. 1.2.840.10008.1.2.4.203 RLELosslessTransferSyntax 1.2.840.10008.1.2.5 +DeflatedImageFrameCompressionTransferSyntax 1.2.840.10008.1.2.8.1 .fi .PP .PP @@ -706,6 +846,7 @@ EXITCODE_NO_PRESENTATION_CONTEXT 66 EXITCODE_CANNOT_CLOSE_ASSOCIATION 67 EXITCODE_CMOVE_WARNING 68 EXITCODE_CMOVE_ERROR 69 +EXITCODE_CANNOT_CREATE_TLS_LAYER 70 .fi .PP .SH "ENVIRONMENT" @@ -718,4 +859,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBfindscu\fP(1), \fBstorescp\fP(1), \fBdump2dcm\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1994-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1994-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/pdf2dcm.1 b/doxygen/manpages/man1/pdf2dcm.1 index 6dc700d1..d59c1633 100644 --- a/doxygen/manpages/man1/pdf2dcm.1 +++ b/doxygen/manpages/man1/pdf2dcm.1 @@ -1,4 +1,4 @@ -.TH "pdf2dcm" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "pdf2dcm" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME pdf2dcm \- Encapsulate PDF file into DICOM file format @@ -12,214 +12,10 @@ pdf2dcm [options] pdffile-in dcmfile-out .PP .SH "DESCRIPTION" .PP -The \fBpdf2dcm\fP utility reads a PDF file (\fIpdffile-in\fP), converts it to a DICOM Encapsulated PDF Storage SOP instance and stores the converted data to an output file (\fIdcmfile-out\fP)\&. -.SH "PARAMETERS" -.PP -.PP -.nf -pdffile-in PDF input filename to be encapsulated - -dcmfile-out DICOM output filename ("-" for stdout) -.fi -.PP -.SH "OPTIONS" -.PP -.SS "general options" -.PP -.nf - -h --help - print this help text and exit - - --version - print version information and exit - - --arguments - print expanded command line arguments - - -q --quiet - quiet mode, print no warnings and errors - - -v --verbose - verbose mode, print processing details - - -d --debug - debug mode, print debug information - - -ll --log-level [l]evel: string constant - (fatal, error, warn, info, debug, trace) - use level l for the logger - - -lc --log-config [f]ilename: string - use config file f for the logger -.fi -.PP -.SS "DICOM document options" -.PP -.nf -document title: - - +t --title [t]itle: string (default: empty) - document title - - +cn --concept-name [CSD] [CV] [CM]: string (default: empty) - coded representation of document title defined by coding - scheme designator CSD, code value CV and code meaning CM - -patient data: - - +pn --patient-name [n]ame: string - patient's name in DICOM PN syntax - - +pi --patient-id [i]d: string - patient identifier - - +pb --patient-birthdate [d]ate: string (YYYYMMDD) - patient's birth date - - +ps --patient-sex [s]ex: string (M, F or O) - patient's sex - -study and series: - - +sg --generate - generate new study and series UIDs (default) - - +st --study-from [f]ilename: string - read patient/study data from DICOM file - - +se --series-from [f]ilename: string - read patient/study/series data from DICOM file - -instance number: - - +i1 --instance-one - use instance number 1 (default, not with +se) - - +ii --instance-inc - increment instance number (only with +se) - - +is --instance-set [i]nstance number: integer - use instance number i - -burned-in annotation: - - +an --annotation-yes - document contains patient identifying data (default) - - -an --annotation-no - document does not contain patient identifying data -.fi -.PP -.SS "processing options" -.PP -.nf -other processing options: - - -k --key [k]ey: gggg,eeee="str", path or dictionary name="str" - add further attribute -.fi -.PP -.SS "output options" -.PP -.nf -output file format: - - +F --write-file - write file format (default) - - -F --write-dataset - write data set without file meta information - -group length encoding: - - +g= --group-length-recalc - recalculate group lengths if present (default) - - +g --group-length-create - always write with group length elements - - -g --group-length-remove - always write without group length elements - -length encoding in sequences and items: - - +e --length-explicit - write with explicit lengths (default) - - -e --length-undefined - write with undefined lengths - -data set trailing padding (not with --write-dataset): - - -p --padding-off - no padding (implicit if --write-dataset) - - +p --padding-create [f]ile-pad [i]tem-pad: integer - align file on multiple of f bytes - and items on multiple of i bytes -.fi -.PP -.SH "NOTES" -.PP -.SS "Attribute Sources" -The application may be fed with some additional input for filling mandatory (and optional) attributes in the new DICOM file like patient, study and series information: -.PP -.IP "\(bu" 2 -The \fI--key\fP option can be used to add further attributes to the DICOM output file\&. -.IP "\(bu" 2 -It is also possible to specify sequences, items and nested attributes using the \fI--key\fP option\&. In these cases, a special 'path' notation has to be used\&. Details on this path notation can be found in the documentation of \fBdcmodify\fP\&. -.IP "\(bu" 2 -The \fI--key\fP option can be present more than once\&. -.IP "\(bu" 2 -The value part (after the '=') may be absent causing the attribute to be set with zero length\&. -.IP "\(bu" 2 -Please be advised that the \fI--key\fP option is applied at the very end, just before saving the DICOM file, so there is no value checking whatsoever\&. -.PP -.SH "LOGGING" -.PP -The level of logging output of the various command line tools and underlying libraries can be specified by the user\&. By default, only errors and warnings are written to the standard error stream\&. Using option \fI--verbose\fP also informational messages like processing details are reported\&. Option \fI--debug\fP can be used to get more details on the internal activity, e\&.g\&. for debugging purposes\&. Other logging levels can be selected using option \fI--log-level\fP\&. In \fI--quiet\fP mode only fatal errors are reported\&. In such very severe error events, the application will usually terminate\&. For more details on the different logging levels, see documentation of module 'oflog'\&. -.PP -In case the logging output should be written to file (optionally with logfile rotation), to syslog (Unix) or the event log (Windows) option \fI--log-config\fP can be used\&. This configuration file also allows for directing only certain messages to a particular output stream and for filtering certain messages based on the module or application where they are generated\&. An example configuration file is provided in \fI/logger\&.cfg\fP\&. -.SH "COMMAND LINE" -.PP -All command line tools use the following notation for parameters: square brackets enclose optional values (0-1), three trailing dots indicate that multiple values are allowed (1-n), a combination of both means 0 to n values\&. -.PP -Command line options are distinguished from parameters by a leading '+' or '-' sign, respectively\&. Usually, order and position of command line options are arbitrary (i\&.e\&. they can appear anywhere)\&. However, if options are mutually exclusive the rightmost appearance is used\&. This behavior conforms to the standard evaluation rules of common Unix shells\&. -.PP -In addition, one or more command files can be specified using an '@' sign as a prefix to the filename (e\&.g\&. \fI@command\&.txt\fP)\&. Such a command argument is replaced by the content of the corresponding text file (multiple whitespaces are treated as a single separator unless they appear between two quotation marks) prior to any further evaluation\&. Please note that a command file cannot contain another command file\&. This simple but effective approach allows one to summarize common combinations of options/parameters and avoids longish and confusing command lines (an example is provided in file \fI/dumppat\&.txt\fP)\&. -.SH "EXIT CODES" -.PP -The \fBpdf2dcm\fP utility uses the following exit codes when terminating\&. This enables the user to check for the reason why the application terminated\&. -.SS "general" -.PP -.nf -EXITCODE_NO_ERROR 0 -EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 -EXITCODE_MEMORY_EXHAUSTED 4 -.fi -.PP -.SS "input file errors" -.PP -.nf -EXITCODE_CANNOT_READ_INPUT_FILE 20 -EXITCODE_NO_INPUT_FILES 21 -EXITCODE_INVALID_INPUT_FILE 22 -.fi -.PP -.SS "output file errors" -.PP -.nf -EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 -.fi -.PP -.SH "ENVIRONMENT" -.PP -The \fBpdf2dcm\fP utility will attempt to load DICOM data dictionaries specified in the \fIDCMDICTPATH\fP environment variable\&. By default, i\&.e\&. if the \fIDCMDICTPATH\fP environment variable is not set, the file \fI/dicom\&.dic\fP will be loaded unless the dictionary is built into the application (default for Windows)\&. -.PP -The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. +The \fBpdf2dcm\fP tool is deprecated\&. Use \fBdcmencap\fP instead, which supports the same command line parameters, and more\&. .SH "SEE ALSO" .PP -\fBdcm2pdf\fP(1) +\fBdcmencap\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2005-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2005-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/stl2dcm.1 b/doxygen/manpages/man1/stl2dcm.1 index 49db5cc9..24e3e3ed 100644 --- a/doxygen/manpages/man1/stl2dcm.1 +++ b/doxygen/manpages/man1/stl2dcm.1 @@ -1,4 +1,4 @@ -.TH "stl2dcm" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "stl2dcm" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME stl2dcm \- Encapsulate STL file into DICOM file format @@ -12,231 +12,10 @@ stl2dcm [options] stlfile-in dcmfile-out .PP .SH "DESCRIPTION" .PP -The \fBstl2dcm\fP utility reads a STL file (\fIstlfile-in\fP), converts it to a DICOM Encapsulated STL Storage SOP instance and stores the converted data to an output file (\fIdcmfile-out\fP)\&. -.SH "PARAMETERS" +The \fBstl2dcm\fP tool is deprecated\&. Use \fBdcmencap\fP instead, which supports the same command line parameters, and more\&. +.SH "SEE ALSO" .PP -.PP -.nf -stlfile-in STL input filename to be encapsulated - -dcmfile-out DICOM output filename ("-" for stdout) -.fi -.PP -.SH "OPTIONS" -.PP -.SS "general options" -.PP -.nf - -h --help - print this help text and exit - - --version - print version information and exit - - --arguments - print expanded command line arguments - - -q --quiet - quiet mode, print no warnings and errors - - -v --verbose - verbose mode, print processing details - - -d --debug - debug mode, print debug information - - -ll --log-level [l]evel: string constant - (fatal, error, warn, info, debug, trace) - use level l for the logger - - -lc --log-config [f]ilename: string - use config file f for the logger -.fi -.PP -.SS "DICOM document options" -.PP -.nf -document title: - - +t --title [t]itle: string (default: empty) - document title - - +cn --concept-name [CSD] [CV] [CM]: string (default: empty) - coded representation of document title defined by coding - scheme designator CSD, code value CV and code meaning CM - -patient data: - - +pn --patient-name [n]ame: string - patient's name in DICOM PN syntax - - +pi --patient-id [i]d: string - patient identifier - - +pb --patient-birthdate [d]ate: string (YYYYMMDD) - patient's birth date - - +ps --patient-sex [s]ex: string (M, F or O) - patient's sex - -study and series: - - +sg --generate - generate new study and series UIDs (default) - - +st --study-from [f]ilename: string - read patient/study data from DICOM file - - +se --series-from [f]ilename: string - read patient/study/series data from DICOM file - -instance number: - - +i1 --instance-one - use instance number 1 (default, not with +se) - - +ii --instance-inc - increment instance number (only with +se) - - +is --instance-set [i]nstance number: integer - use instance number i - -burned-in annotation: - - +an --annotation-yes - document contains patient identifying data (default) - - -an --annotation-no - document does not contain patient identifying data - -enhanced general equipment: - - +mn --manufacturer [n]ame: string - manufacturer's name - - +mm --manufacturer-model [n]ame: string - manufacturer's model name - - +ds --device-serial [n]umber: string - device serial number - - +sv --software-versions [v]ersions: string - software versions - -3d model measurement units: - - +mu --measurement-units [CSD] [CV] [CM]: string - measurement units with coding scheme designator CSD, - code value CV and code meaning CM (default: UCUM, um, um) -.fi -.PP -.SS "processing options" -.PP -.nf -other processing options: - - -k --key [k]ey: gggg,eeee="str", path or dictionary name="str" - add further attribute -.fi -.PP -.SS "output options" -.PP -.nf -output file format: - - +F --write-file - write file format (default) - - -F --write-dataset - write data set without file meta information - -group length encoding: - - +g= --group-length-recalc - recalculate group lengths if present (default) - - +g --group-length-create - always write with group length elements - - -g --group-length-remove - always write without group length elements - -length encoding in sequences and items: - - +e --length-explicit - write with explicit lengths (default) - - -e --length-undefined - write with undefined lengths - -data set trailing padding (not with --write-dataset): - - -p --padding-off - no padding (implicit if --write-dataset) - - +p --padding-create [f]ile-pad [i]tem-pad: integer - align file on multiple of f bytes - and items on multiple of i bytes -.fi -.PP -.SH "NOTES" -.PP -.SS "Attribute Sources" -The application may be fed with some additional input for filling mandatory (and optional) attributes in the new DICOM file like patient, study and series information: -.PP -.IP "\(bu" 2 -The \fI--key\fP option can be used to add further attributes to the DICOM output file\&. -.IP "\(bu" 2 -It is also possible to specify sequences, items and nested attributes using the \fI--key\fP option\&. In these cases, a special 'path' notation has to be used\&. Details on this path notation can be found in the documentation of \fBdcmodify\fP\&. -.IP "\(bu" 2 -The \fI--key\fP option can be present more than once\&. -.IP "\(bu" 2 -The value part (after the '=') may be absent causing the attribute to be set with zero length\&. -.IP "\(bu" 2 -Please be advised that the \fI--key\fP option is applied at the very end, just before saving the DICOM file, so there is no value checking whatsoever\&. -.PP -.SH "LOGGING" -.PP -The level of logging output of the various command line tools and underlying libraries can be specified by the user\&. By default, only errors and warnings are written to the standard error stream\&. Using option \fI--verbose\fP also informational messages like processing details are reported\&. Option \fI--debug\fP can be used to get more details on the internal activity, e\&.g\&. for debugging purposes\&. Other logging levels can be selected using option \fI--log-level\fP\&. In \fI--quiet\fP mode only fatal errors are reported\&. In such very severe error events, the application will usually terminate\&. For more details on the different logging levels, see documentation of module 'oflog'\&. -.PP -In case the logging output should be written to file (optionally with logfile rotation), to syslog (Unix) or the event log (Windows) option \fI--log-config\fP can be used\&. This configuration file also allows for directing only certain messages to a particular output stream and for filtering certain messages based on the module or application where they are generated\&. An example configuration file is provided in \fI/logger\&.cfg\fP\&. -.SH "COMMAND LINE" -.PP -All command line tools use the following notation for parameters: square brackets enclose optional values (0-1), three trailing dots indicate that multiple values are allowed (1-n), a combination of both means 0 to n values\&. -.PP -Command line options are distinguished from parameters by a leading '+' or '-' sign, respectively\&. Usually, order and position of command line options are arbitrary (i\&.e\&. they can appear anywhere)\&. However, if options are mutually exclusive the rightmost appearance is used\&. This behavior conforms to the standard evaluation rules of common Unix shells\&. -.PP -In addition, one or more command files can be specified using an '@' sign as a prefix to the filename (e\&.g\&. \fI@command\&.txt\fP)\&. Such a command argument is replaced by the content of the corresponding text file (multiple whitespaces are treated as a single separator unless they appear between two quotation marks) prior to any further evaluation\&. Please note that a command file cannot contain another command file\&. This simple but effective approach allows one to summarize common combinations of options/parameters and avoids longish and confusing command lines (an example is provided in file \fI/dumppat\&.txt\fP)\&. -.SH "EXIT CODES" -.PP -The \fBstl2dcm\fP utility uses the following exit codes when terminating\&. This enables the user to check for the reason why the application terminated\&. -.SS "general" -.PP -.nf -EXITCODE_NO_ERROR 0 -EXITCODE_COMMANDLINE_SYNTAX_ERROR 1 -EXITCODE_MEMORY_EXHAUSTED 4 -.fi -.PP -.SS "input file errors" -.PP -.nf -EXITCODE_CANNOT_READ_INPUT_FILE 20 -EXITCODE_NO_INPUT_FILES 21 -EXITCODE_INVALID_INPUT_FILE 22 -.fi -.PP -.SS "output file errors" -.PP -.nf -EXITCODE_CANNOT_WRITE_OUTPUT_FILE 40 -.fi -.PP -.SH "ENVIRONMENT" -.PP -The \fBstl2dcm\fP utility will attempt to load DICOM data dictionaries specified in the \fIDCMDICTPATH\fP environment variable\&. By default, i\&.e\&. if the \fIDCMDICTPATH\fP environment variable is not set, the file \fI/dicom\&.dic\fP will be loaded unless the dictionary is built into the application (default for Windows)\&. -.PP -The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. +\fBdcmencap\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2018-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2018-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/storescp.1 b/doxygen/manpages/man1/storescp.1 index daca35eb..8d77dc2c 100644 --- a/doxygen/manpages/man1/storescp.1 +++ b/doxygen/manpages/man1/storescp.1 @@ -1,4 +1,4 @@ -.TH "storescp" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "storescp" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME storescp \- DICOM storage (C-STORE) SCP @@ -63,11 +63,25 @@ port tcp/ip port number to listen on --fork fork child process for each association + + --max-associations [m]ax: integer (default: unlimited) + limit number of parallel associations to m .fi .PP .SS "network options" .PP .nf +IP protocol version: + + -i4 --ipv4 + use IPv4 only (default) + + -i6 --ipv6 + use IPv6 only + + -i0 --ip-auto + use IPv6/IPv4 dual stack + association negotiation profile from configuration file: -xf --config-file [f]ilename [p]rofile: string @@ -609,6 +623,8 @@ ElectromyogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7 ElectrooculogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7.3 SleepElectroencephalogramWaveformStorage 1.2.840.10008.5.1.4.1.1.9.7.4 BodyPositionWaveformStorage 1.2.840.10008.5.1.4.1.1.9.8.1 +WaveformPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.1 +WaveformAcquisitionPresentationStateStorage 1.2.840.10008.5.1.4.1.1.9.100.2 RETIRED_StandaloneModalityLUTStorage 1.2.840.10008.5.1.4.1.1.10 RETIRED_StandaloneVOILUTStorage 1.2.840.10008.5.1.4.1.1.11 GrayscaleSoftcopyPresentationStateStorage 1.2.840.10008.5.1.4.1.1.11.1 @@ -756,6 +772,7 @@ DICONDE_EddyCurrentImageStorage 1.2.840.10008.5.1.4.1.1.601 DICONDE_EddyCurrentMultiframeImageStorage 1.2.840.10008.5.1.4.1.1.601.2 DICONDE_ThermographyImageStorage 1.2.840.10008.5.1.4.1.1.601.3 DICONDE_ThermographyMultiFrameImageStorage 1.2.840.10008.5.1.4.1.1.601.4 +DICONDE_UltrasoundWaveformStorage 1.2.840.10008.5.1.4.1.1.601.5 DRAFT_RTBeamsDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.1 RTBeamsDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.7 RTBrachyApplicationSetupDeliveryInstructionStorage 1.2.840.10008.5.1.4.34.10 @@ -811,6 +828,7 @@ HighThroughputJPEG2000ImageCompressionLossless.Tr.S. 1.2.840.10008.1.2.4.201 HighThroughputJPEG2000RPCLImageCompressionLoss.Tr.S. 1.2.840.10008.1.2.4.202 HighThroughputJPEG2000ImageCompressionTransferSynta. 1.2.840.10008.1.2.4.203 RLELosslessTransferSyntax 1.2.840.10008.1.2.5 +DeflatedImageFrameCompressionTransferSyntax 1.2.840.10008.1.2.8.1 .fi .PP .PP @@ -863,4 +881,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBstorescu\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1996-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1996-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/storescu.1 b/doxygen/manpages/man1/storescu.1 index cbb1ad7c..90d8b31a 100644 --- a/doxygen/manpages/man1/storescu.1 +++ b/doxygen/manpages/man1/storescu.1 @@ -1,4 +1,4 @@ -.TH "storescu" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "storescu" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME storescu \- DICOM storage (C-STORE) SCU @@ -562,4 +562,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBstorescp\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 1996-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1996-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/termscu.1 b/doxygen/manpages/man1/termscu.1 index 139450fd..9c814d47 100644 --- a/doxygen/manpages/man1/termscu.1 +++ b/doxygen/manpages/man1/termscu.1 @@ -1,4 +1,4 @@ -.TH "termscu" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "termscu" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME termscu \- DICOM termination SCU @@ -110,4 +110,4 @@ The \fBtermscu\fP utility will attempt to load DICOM data dictionaries specified The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. .SH "COPYRIGHT" .PP -Copyright (C) 2005-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2005-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/wlmscpfs.1 b/doxygen/manpages/man1/wlmscpfs.1 index 81f94afd..32cad82c 100644 --- a/doxygen/manpages/man1/wlmscpfs.1 +++ b/doxygen/manpages/man1/wlmscpfs.1 @@ -1,4 +1,4 @@ -.TH "wlmscpfs" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "wlmscpfs" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME wlmscpfs \- DICOM Basic Worklist Management SCP (based on data files) @@ -102,6 +102,17 @@ other processing options: .SS "network options" .PP .nf +IP protocol version: + + -i4 --ipv4 + use IPv4 only (default) + + -i6 --ipv6 + use IPv6 only + + -i0 --ip-auto + use IPv6/IPv4 dual stack + preferred network transfer syntaxes: +x= --prefer-uncompr @@ -334,7 +345,7 @@ As return keys the following attributes are currently supported by \fBwlmscpfs:\ (0010,1080) MilitaryRank (0010,2000) MedicalAlerts (0010,2110) ContrastAllergies -(0010,2160) EthnicGroup +(0010,2160) EthnicGroup (retired) (0010,21a0) SmokingStatus (0010,21b0) AdditionalPatientHistory (0010,21c0) PregnancyStatus @@ -421,4 +432,4 @@ The \fBwlmscpfs\fP utility will attempt to load DICOM data dictionaries specifie The default behavior should be preferred and the \fIDCMDICTPATH\fP environment variable only used when alternative data dictionaries are required\&. The \fIDCMDICTPATH\fP environment variable has the same format as the Unix shell \fIPATH\fP variable in that a colon (':') separates entries\&. On Windows systems, a semicolon (';') is used as a separator\&. The data dictionary code will attempt to load each file specified in the \fIDCMDICTPATH\fP environment variable\&. It is an error if no data dictionary can be loaded\&. .SH "COPYRIGHT" .PP -Copyright (C) 1996-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 1996-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/xml2dcm.1 b/doxygen/manpages/man1/xml2dcm.1 index 03075dba..31b2717e 100644 --- a/doxygen/manpages/man1/xml2dcm.1 +++ b/doxygen/manpages/man1/xml2dcm.1 @@ -1,4 +1,4 @@ -.TH "xml2dcm" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "xml2dcm" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME xml2dcm \- Convert XML document to DICOM file or data set @@ -284,4 +284,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdcm2xml\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2003-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2003-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/doxygen/manpages/man1/xml2dsr.1 b/doxygen/manpages/man1/xml2dsr.1 index d3b58871..b19a07c9 100644 --- a/doxygen/manpages/man1/xml2dsr.1 +++ b/doxygen/manpages/man1/xml2dsr.1 @@ -1,4 +1,4 @@ -.TH "xml2dsr" 1 "Wed Dec 11 2024" "Version 3.6.9" "OFFIS DCMTK" \" -*- nroff -*- +.TH "xml2dsr" 1 "Mon Dec 15 2025" "Version 3.7.0" "OFFIS DCMTK" \" -*- nroff -*- .nh .SH NAME xml2dsr \- Convert XML document to DICOM SR file @@ -190,9 +190,13 @@ PatientRadiationDoseSRStorage 1.2.840.10008.5.1.4.1.1.88.73 PlannedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.74 PerformedImagingAgentAdministrationSRStorage 1.2.840.10008.5.1.4.1.1.88.75 WaveformAnnotationSRStorage 1.2.840.10008.5.1.4.1.1.88.77 + +RenditionSelectionDocumentRealTimeCommunication 1.2.840.10008.10.4 (*) .fi .PP .PP +(*) This is not a Storage SOP Class, but used for Real-Time Communication\&. +.PP Please note that currently only mandatory and some optional attributes are supported\&. .SS "Character Encoding" The DICOM character encoding is determined automatically from the element with tag '0008,0005' (Specific Character Set) - if present\&. The following character sets are currently supported (requires \fBlibxml\fP to include \fBiconv\fP support, see \fI--version\fP output): @@ -250,4 +254,4 @@ The default behavior should be preferred and the \fIDCMDICTPATH\fP environment v \fBdsr2xml\fP(1) .SH "COPYRIGHT" .PP -Copyright (C) 2003-2024 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. +Copyright (C) 2003-2025 by OFFIS e\&.V\&., Escherweg 2, 26121 Oldenburg, Germany\&. diff --git a/oficonv/apps/mkcsmapper.y b/oficonv/apps/mkcsmapper.y index 508edd64..e7630044 100644 --- a/oficonv/apps/mkcsmapper.y +++ b/oficonv/apps/mkcsmapper.y @@ -28,9 +28,7 @@ */ #include "dcmtk/config/osconfig.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif #include #include #include @@ -593,7 +591,7 @@ set_src_zone(uint32_t val) if (rowcol_len <= 32 / rowcol_bits) break; /*FALLTHROUGH*/ - default: + default: goto bad; } rowcol_mask = 1u << (rowcol_bits - 1); diff --git a/oficonv/apps/mkcsmapper_bison.cc b/oficonv/apps/mkcsmapper_bison.cc index 7dd4017c..61e007ff 100644 --- a/oficonv/apps/mkcsmapper_bison.cc +++ b/oficonv/apps/mkcsmapper_bison.cc @@ -1,4 +1,4 @@ -/* A Bison parser, made by GNU Bison 3.7.5. */ +/* A Bison parser, made by GNU Bison 3.8.2. */ /* Bison implementation for Yacc-like parsers in C @@ -16,7 +16,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . */ + along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -46,10 +46,10 @@ USER NAME SPACE" below. */ /* Identify Bison output, and Bison version. */ -#define YYBISON 30705 +#define YYBISON 30802 /* Bison version string. */ -#define YYBISON_VERSION "3.7.5" +#define YYBISON_VERSION "3.8.2" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -98,9 +98,7 @@ */ #include "dcmtk/config/osconfig.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif #include #include #include @@ -303,7 +301,7 @@ static void put32(void *, size_t, uint32_t); static void set_range(uint32_t, uint32_t); static void set_src(linear_zone_t *, uint32_t, uint32_t); -#line 307 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 305 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" # ifndef YY_CAST # ifdef __cplusplus @@ -531,12 +529,18 @@ typedef int yy_state_fast_t; # define YY_USE(E) /* empty */ #endif -#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ /* Suppress an incorrect diagnostic about yylval being uninitialized. */ -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ +#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ +# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") +# else +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# endif # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else @@ -752,14 +756,14 @@ static const yytype_int8 yytranslate[] = }; #if YYDEBUG - /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_int16 yyrline[] = { - 0, 256, 256, 259, 260, 261, 262, 263, 264, 265, - 266, 267, 269, 270, 271, 272, 274, 275, 277, 278, - 281, 285, 286, 287, 288, 290, 291, 293, 294, 296, - 297, 299, 301, 303, 307, 311, 317, 320, 324, 328, - 332, 333 + 0, 254, 254, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 267, 268, 269, 270, 272, 273, 275, 276, + 279, 283, 284, 285, 286, 288, 289, 291, 292, 294, + 295, 297, 299, 301, 305, 309, 315, 318, 322, 326, + 330, 331 }; #endif @@ -792,17 +796,6 @@ yysymbol_name (yysymbol_kind_t yysymbol) } #endif -#ifdef YYPRINT -/* YYTOKNUM[NUM] -- (External) token number corresponding to the - (internal) symbol number NUM (which must be that of a token). */ -static const yytype_int16 yytoknum[] = -{ - 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, - 265, 266, 267, 268, 269, 270, 271, 272, 45, 47, - 61 -}; -#endif - #define YYPACT_NINF (-39) #define yypact_value_is_default(Yyn) \ @@ -813,8 +806,8 @@ static const yytype_int16 yytoknum[] = #define yytable_value_is_error(Yyn) \ 0 - /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ static const yytype_int8 yypact[] = { -39, 12, -1, -39, 3, 4, 7, 9, 10, 11, @@ -826,9 +819,9 @@ static const yytype_int8 yypact[] = 14, 25, -39 }; - /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. - Performed when YYTABLE does not specify something else to do. Zero - means the default is an error. */ +/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ static const yytype_int8 yydefact[] = { 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, @@ -840,7 +833,7 @@ static const yytype_int8 yydefact[] = 20, 0, 17 }; - /* YYPGOTO[NTERM-NUM]. */ +/* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -39, -39, -39, -39, -39, -39, -38, -39, -39, -39, @@ -848,7 +841,7 @@ static const yytype_int8 yypgoto[] = -39, -20 }; - /* YYDEFGOTO[NTERM-NUM]. */ +/* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { 0, 1, 2, 13, 14, 23, 26, 58, 15, 27, @@ -856,9 +849,9 @@ static const yytype_int8 yydefgoto[] = 45, 32 }; - /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule whose - number is the opposite. If YYTABLE_NINF, syntax error. */ +/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_int8 yytable[] = { 36, 47, 4, 5, 6, 7, 8, 9, 10, 33, @@ -877,8 +870,8 @@ static const yytype_int8 yycheck[] = 20, 19, 18, -1, 19 }; - /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing - symbol of state STATE-NUM. */ +/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of + state STATE-NUM. */ static const yytype_int8 yystos[] = { 0, 22, 23, 0, 3, 4, 5, 6, 7, 8, @@ -890,7 +883,7 @@ static const yytype_int8 yystos[] = 16, 27, 19 }; - /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ static const yytype_int8 yyr1[] = { 0, 21, 22, 23, 23, 23, 23, 23, 23, 23, @@ -900,7 +893,7 @@ static const yytype_int8 yyr1[] = 42, 42 }; - /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ static const yytype_int8 yyr2[] = { 0, 2, 3, 0, 2, 2, 2, 2, 2, 2, @@ -919,6 +912,7 @@ enum { YYENOMEM = -2 }; #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab +#define YYNOMEM goto yyexhaustedlab #define YYRECOVERING() (!!yyerrstatus) @@ -959,10 +953,7 @@ do { \ YYFPRINTF Args; \ } while (0) -/* This macro is provided for backward compatibility. */ -# ifndef YY_LOCATION_PRINT -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif + # define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ @@ -989,10 +980,6 @@ yy_symbol_value_print (FILE *yyo, YY_USE (yyoutput); if (!yyvaluep) return; -# ifdef YYPRINT - if (yykind < YYNTOKENS) - YYPRINT (yyo, yytoknum[yykind], *yyvaluep); -# endif YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN YY_USE (yykind); YY_IGNORE_MAYBE_UNINITIALIZED_END @@ -1177,6 +1164,7 @@ yyparse (void) YYDPRINTF ((stderr, "Starting parse\n")); yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; @@ -1202,7 +1190,7 @@ yysetstate: if (yyss + yystacksize - 1 <= yyssp) #if !defined yyoverflow && !defined YYSTACK_RELOCATE - goto yyexhaustedlab; + YYNOMEM; #else { /* Get the current used size of the three stacks, in elements. */ @@ -1230,7 +1218,7 @@ yysetstate: # else /* defined YYSTACK_RELOCATE */ /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) - goto yyexhaustedlab; + YYNOMEM; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; @@ -1241,7 +1229,7 @@ yysetstate: YY_CAST (union yyalloc *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); if (! yyptr) - goto yyexhaustedlab; + YYNOMEM; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE @@ -1263,6 +1251,7 @@ yysetstate: } #endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + if (yystate == YYFINAL) YYACCEPT; @@ -1375,169 +1364,169 @@ yyreduce: switch (yyn) { case 2: /* file: property mapping lns */ -#line 257 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 255 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { dump_file(); } -#line 1381 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1370 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 12: /* name: R_NAME L_STRING */ -#line 269 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 267 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { set_name((yyvsp[0].s_value)); (yyvsp[0].s_value) = NULL; } -#line 1387 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1376 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 13: /* type: R_TYPE types */ -#line 270 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 268 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { set_type((yyvsp[0].i_value)); } -#line 1393 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1382 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 14: /* types: R_ROWCOL */ -#line 271 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 269 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { (yyval.i_value) = R_ROWCOL; } -#line 1399 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1388 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 15: /* range: L_IMM '-' L_IMM */ -#line 272 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 270 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { set_range((yyvsp[-2].i_value), (yyvsp[0].i_value)); } -#line 1405 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1394 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 18: /* src_zone: R_SRC_ZONE zone */ -#line 277 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 275 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { set_src_zone((yyvsp[0].i_value)); } -#line 1411 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1400 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 19: /* zone: range */ -#line 278 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 276 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { (yyval.i_value) = 32; } -#line 1419 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1408 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 20: /* zone: range '/' range '/' ranges L_IMM */ -#line 281 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 279 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { (yyval.i_value) = (yyvsp[0].i_value); } -#line 1427 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1416 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 21: /* dst_invalid: R_DST_INVALID L_IMM */ -#line 285 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 283 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { set_dst_invalid((yyvsp[0].i_value)); } -#line 1433 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1422 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 22: /* dst_ilseq: R_DST_ILSEQ L_IMM */ -#line 286 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 284 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { set_dst_ilseq((yyvsp[0].i_value)); } -#line 1439 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1428 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 23: /* dst_unit_bits: R_DST_UNIT_BITS L_IMM */ -#line 287 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 285 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { set_dst_unit_bits((yyvsp[0].i_value)); } -#line 1445 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1434 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 24: /* oob_mode: R_OOB_MODE oob_mode_sel */ -#line 288 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 286 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { set_oob_mode((yyvsp[0].i_value)); } -#line 1451 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1440 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 25: /* oob_mode_sel: R_INVALID */ -#line 290 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 288 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { (yyval.i_value) = _CITRUS_MAPPER_STD_OOB_NONIDENTICAL; } -#line 1457 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1446 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 26: /* oob_mode_sel: R_ILSEQ */ -#line 291 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 289 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { (yyval.i_value) = _CITRUS_MAPPER_STD_OOB_ILSEQ; } -#line 1463 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1452 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 28: /* begin_map: R_BEGIN_MAP lns */ -#line 294 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 292 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { setup_map(); } -#line 1469 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1458 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 31: /* map_elem: src '=' dst */ -#line 300 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 298 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { store(&(yyvsp[-2].lz_value), (yyvsp[0].i_value), 0); } -#line 1475 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1464 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 32: /* map_elem: src '=' L_IMM '-' */ -#line 302 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 300 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { store(&(yyvsp[-3].lz_value), (yyvsp[-1].i_value), 1); } -#line 1481 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1470 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 33: /* dst: L_IMM */ -#line 304 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 302 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { (yyval.i_value) = (yyvsp[0].i_value); } -#line 1489 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1478 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 34: /* dst: R_INVALID */ -#line 308 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 306 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { (yyval.i_value) = dst_invalid; } -#line 1497 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1486 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 35: /* dst: R_ILSEQ */ -#line 312 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 310 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { (yyval.i_value) = dst_ilseq; } -#line 1505 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1494 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 36: /* src: %empty */ -#line 317 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 315 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { set_src(&(yyval.lz_value), src_next, src_next); } -#line 1513 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1502 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 37: /* src: L_IMM */ -#line 321 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 319 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { set_src(&(yyval.lz_value), (yyvsp[0].i_value), (yyvsp[0].i_value)); } -#line 1521 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1510 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 38: /* src: L_IMM '-' L_IMM */ -#line 325 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 323 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { set_src(&(yyval.lz_value), (yyvsp[-2].i_value), (yyvsp[0].i_value)); } -#line 1529 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1518 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; case 39: /* src: '-' L_IMM */ -#line 329 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 327 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" { set_src(&(yyval.lz_value), src_next, (yyvsp[0].i_value)); } -#line 1537 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1526 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" break; -#line 1541 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" +#line 1530 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper_bison.cc" default: break; } @@ -1619,6 +1608,7 @@ yyerrorlab: label yyerrorlab therefore never appears in user code. */ if (0) YYERROR; + ++yynerrs; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ @@ -1679,7 +1669,7 @@ yyerrlab1: `-------------------------------------*/ yyacceptlab: yyresult = 0; - goto yyreturn; + goto yyreturnlab; /*-----------------------------------. @@ -1687,24 +1677,22 @@ yyacceptlab: `-----------------------------------*/ yyabortlab: yyresult = 1; - goto yyreturn; + goto yyreturnlab; -#if !defined yyoverflow -/*-------------------------------------------------. -| yyexhaustedlab -- memory exhaustion comes here. | -`-------------------------------------------------*/ +/*-----------------------------------------------------------. +| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | +`-----------------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; - goto yyreturn; -#endif + goto yyreturnlab; -/*-------------------------------------------------------. -| yyreturn -- parsing is finished, clean up and return. | -`-------------------------------------------------------*/ -yyreturn: +/*----------------------------------------------------------. +| yyreturnlab -- parsing is finished, clean up and return. | +`----------------------------------------------------------*/ +yyreturnlab: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at @@ -1731,7 +1719,7 @@ yyreturn: return yyresult; } -#line 335 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 333 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" static void @@ -1993,7 +1981,7 @@ set_src_zone(uint32_t val) if (rowcol_len <= 32 / rowcol_bits) break; /*FALLTHROUGH*/ - default: + default: goto bad; } rowcol_mask = 1u << (rowcol_bits - 1); diff --git a/oficonv/apps/mkcsmapper_bison.h b/oficonv/apps/mkcsmapper_bison.h index 05224eae..7bbfbc37 100644 --- a/oficonv/apps/mkcsmapper_bison.h +++ b/oficonv/apps/mkcsmapper_bison.h @@ -1,4 +1,4 @@ -/* A Bison parser, made by GNU Bison 3.7.5. */ +/* A Bison parser, made by GNU Bison 3.8.2. */ /* Bison interface for Yacc-like parsers in C @@ -16,7 +16,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . */ + along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -77,7 +77,7 @@ extern int yydebug; #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { -#line 237 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" +#line 235 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkcsmapper.y" uint32_t i_value; char *s_value; @@ -94,6 +94,8 @@ typedef union YYSTYPE YYSTYPE; extern YYSTYPE yylval; + int yyparse (void); + #endif /* !YY_YY_HOME_MEICHEL_DICOM_DCMTK_FULL_PUBLIC_OFICONV_APPS_MKCSMAPPER_BISON_H_INCLUDED */ diff --git a/oficonv/apps/mkesdb.y b/oficonv/apps/mkesdb.y index 333d610b..dfb45035 100644 --- a/oficonv/apps/mkesdb.y +++ b/oficonv/apps/mkesdb.y @@ -28,9 +28,7 @@ */ #include "dcmtk/config/osconfig.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_QUEUE_H #include #else diff --git a/oficonv/apps/mkesdb_bison.cc b/oficonv/apps/mkesdb_bison.cc index 51f29626..978db3ec 100644 --- a/oficonv/apps/mkesdb_bison.cc +++ b/oficonv/apps/mkesdb_bison.cc @@ -1,4 +1,4 @@ -/* A Bison parser, made by GNU Bison 3.7.5. */ +/* A Bison parser, made by GNU Bison 3.8.2. */ /* Bison implementation for Yacc-like parsers in C @@ -16,7 +16,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . */ + along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -46,10 +46,10 @@ USER NAME SPACE" below. */ /* Identify Bison output, and Bison version. */ -#define YYBISON 30705 +#define YYBISON 30802 /* Bison version string. */ -#define YYBISON_VERSION "3.7.5" +#define YYBISON_VERSION "3.8.2" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -98,9 +98,7 @@ */ #include "dcmtk/config/osconfig.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_QUEUE_H #include #else @@ -275,7 +273,7 @@ static void register_named_csid(char *, uint32_t); static void set_invalid(uint32_t); static void set_prop_string(const char *, char **, char **); -#line 279 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" +#line 277 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" # ifndef YY_CAST # ifdef __cplusplus @@ -479,12 +477,18 @@ typedef int yy_state_fast_t; # define YY_USE(E) /* empty */ #endif -#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ /* Suppress an incorrect diagnostic about yylval being uninitialized. */ -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ +#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ +# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") +# else +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# endif # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else @@ -699,11 +703,11 @@ static const yytype_int8 yytranslate[] = }; #if YYDEBUG - /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint8 yyrline[] = { - 0, 220, 220, 223, 224, 225, 226, 227, 228, 229, - 231, 236, 240, 244, 249 + 0, 218, 218, 221, 222, 223, 224, 225, 226, 227, + 229, 234, 238, 242, 247 }; #endif @@ -732,16 +736,6 @@ yysymbol_name (yysymbol_kind_t yysymbol) } #endif -#ifdef YYPRINT -/* YYTOKNUM[NUM] -- (External) token number corresponding to the - (internal) symbol number NUM (which must be that of a token). */ -static const yytype_int16 yytoknum[] = -{ - 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, - 265 -}; -#endif - #define YYPACT_NINF (-4) #define yypact_value_is_default(Yyn) \ @@ -752,8 +746,8 @@ static const yytype_int16 yytoknum[] = #define yytable_value_is_error(Yyn) \ 0 - /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ static const yytype_int8 yypact[] = { -4, 6, -3, -4, -2, -1, 0, 1, 3, -4, @@ -761,9 +755,9 @@ static const yytype_int8 yypact[] = -4, -4, -4, -4, -4, -4 }; - /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. - Performed when YYTABLE does not specify something else to do. Zero - means the default is an error. */ +/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ static const yytype_int8 yydefact[] = { 3, 0, 2, 1, 0, 0, 0, 0, 0, 4, @@ -771,21 +765,21 @@ static const yytype_int8 yydefact[] = 5, 6, 7, 8, 9, 13 }; - /* YYPGOTO[NTERM-NUM]. */ +/* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -4, -4, -4, -4, -4, -4, -4, -4 }; - /* YYDEFGOTO[NTERM-NUM]. */ +/* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { 0, 1, 2, 10, 11, 12, 13, 14 }; - /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule whose - number is the opposite. If YYTABLE_NINF, syntax error. */ +/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_int8 yytable[] = { 4, 5, 6, 7, 8, 9, 3, 0, 15, 16, @@ -800,8 +794,8 @@ static const yytype_int8 yycheck[] = 9 }; - /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing - symbol of state STATE-NUM. */ +/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of + state STATE-NUM. */ static const yytype_int8 yystos[] = { 0, 12, 13, 0, 3, 4, 5, 6, 7, 8, @@ -809,14 +803,14 @@ static const yytype_int8 yystos[] = 8, 8, 8, 8, 8, 9 }; - /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ static const yytype_int8 yyr1[] = { 0, 11, 12, 13, 13, 13, 13, 13, 13, 13, 14, 15, 16, 17, 18 }; - /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ static const yytype_int8 yyr2[] = { 0, 2, 1, 0, 2, 3, 3, 3, 3, 3, @@ -832,6 +826,7 @@ enum { YYENOMEM = -2 }; #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab +#define YYNOMEM goto yyexhaustedlab #define YYRECOVERING() (!!yyerrstatus) @@ -872,10 +867,7 @@ do { \ YYFPRINTF Args; \ } while (0) -/* This macro is provided for backward compatibility. */ -# ifndef YY_LOCATION_PRINT -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif + # define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ @@ -902,10 +894,6 @@ yy_symbol_value_print (FILE *yyo, YY_USE (yyoutput); if (!yyvaluep) return; -# ifdef YYPRINT - if (yykind < YYNTOKENS) - YYPRINT (yyo, yytoknum[yykind], *yyvaluep); -# endif YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN YY_USE (yykind); YY_IGNORE_MAYBE_UNINITIALIZED_END @@ -1090,6 +1078,7 @@ yyparse (void) YYDPRINTF ((stderr, "Starting parse\n")); yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; @@ -1115,7 +1104,7 @@ yysetstate: if (yyss + yystacksize - 1 <= yyssp) #if !defined yyoverflow && !defined YYSTACK_RELOCATE - goto yyexhaustedlab; + YYNOMEM; #else { /* Get the current used size of the three stacks, in elements. */ @@ -1143,7 +1132,7 @@ yysetstate: # else /* defined YYSTACK_RELOCATE */ /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) - goto yyexhaustedlab; + YYNOMEM; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; @@ -1154,7 +1143,7 @@ yysetstate: YY_CAST (union yyalloc *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); if (! yyptr) - goto yyexhaustedlab; + YYNOMEM; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE @@ -1176,6 +1165,7 @@ yysetstate: } #endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + if (yystate == YYFINAL) YYACCEPT; @@ -1288,54 +1278,54 @@ yyreduce: switch (yyn) { case 2: /* file: property */ -#line 221 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" +#line 219 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" { dump_file(); } -#line 1294 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" +#line 1284 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" break; case 10: /* name: R_NAME L_STRING */ -#line 232 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" +#line 230 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" { set_prop_string("NAME", &name, &(yyvsp[0].s_value)); } -#line 1302 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" +#line 1292 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" break; case 11: /* encoding: R_ENCODING L_STRING */ -#line 237 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" +#line 235 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" { set_prop_string("ENCODING", &encoding, &(yyvsp[0].s_value)); } -#line 1310 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" +#line 1300 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" break; case 12: /* variable: R_VARIABLE L_STRING */ -#line 241 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" +#line 239 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" { set_prop_string("VARIABLE", &variable, &(yyvsp[0].s_value)); } -#line 1318 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" +#line 1308 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" break; case 13: /* defcsid: R_DEFCSID L_STRING L_IMM */ -#line 245 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" +#line 243 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" { register_named_csid((yyvsp[-1].s_value), (yyvsp[0].i_value)); (yyvsp[-1].s_value) = NULL; } -#line 1327 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" +#line 1317 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" break; case 14: /* invalid: R_INVALID L_IMM */ -#line 250 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" +#line 248 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" { set_invalid((yyvsp[0].i_value)); } -#line 1335 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" +#line 1325 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" break; -#line 1339 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" +#line 1329 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb_bison.cc" default: break; } @@ -1417,6 +1407,7 @@ yyerrorlab: label yyerrorlab therefore never appears in user code. */ if (0) YYERROR; + ++yynerrs; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ @@ -1477,7 +1468,7 @@ yyerrlab1: `-------------------------------------*/ yyacceptlab: yyresult = 0; - goto yyreturn; + goto yyreturnlab; /*-----------------------------------. @@ -1485,24 +1476,22 @@ yyacceptlab: `-----------------------------------*/ yyabortlab: yyresult = 1; - goto yyreturn; + goto yyreturnlab; -#if !defined yyoverflow -/*-------------------------------------------------. -| yyexhaustedlab -- memory exhaustion comes here. | -`-------------------------------------------------*/ +/*-----------------------------------------------------------. +| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | +`-----------------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; - goto yyreturn; -#endif + goto yyreturnlab; -/*-------------------------------------------------------. -| yyreturn -- parsing is finished, clean up and return. | -`-------------------------------------------------------*/ -yyreturn: +/*----------------------------------------------------------. +| yyreturnlab -- parsing is finished, clean up and return. | +`----------------------------------------------------------*/ +yyreturnlab: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at @@ -1529,7 +1518,7 @@ yyreturn: return yyresult; } -#line 253 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" +#line 251 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" int diff --git a/oficonv/apps/mkesdb_bison.h b/oficonv/apps/mkesdb_bison.h index c3b495d7..a29f762c 100644 --- a/oficonv/apps/mkesdb_bison.h +++ b/oficonv/apps/mkesdb_bison.h @@ -1,4 +1,4 @@ -/* A Bison parser, made by GNU Bison 3.7.5. */ +/* A Bison parser, made by GNU Bison 3.8.2. */ /* Bison interface for Yacc-like parsers in C @@ -16,7 +16,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . */ + along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -70,7 +70,7 @@ extern int yydebug; #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { -#line 208 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" +#line 206 "/home/meichel/dicom/dcmtk-full/public/oficonv/apps/mkesdb.y" uint32_t i_value; char *s_value; @@ -86,6 +86,8 @@ typedef union YYSTYPE YYSTYPE; extern YYSTYPE yylval; + int yyparse (void); + #endif /* !YY_YY_HOME_MEICHEL_DICOM_DCMTK_FULL_PUBLIC_OFICONV_APPS_MKESDB_BISON_H_INCLUDED */ diff --git a/oficonv/docs/oficonv.dox b/oficonv/docs/oficonv.dox index 7456b4a7..47c7aa4e 100644 --- a/oficonv/docs/oficonv.dox +++ b/oficonv/docs/oficonv.dox @@ -34,6 +34,10 @@ if (id != OFreinterpret_cast(iconv_t, -1)) // perform the conversion size_t result = OFiconv(id, &src_ptr, &src_len, &dst_ptr, &dst_len); + + // null terminate + if (dst_len > 0) *dst_ptr = '\0'; + if (result != OFstatic_cast(size_t, -1)) { std::cout << "UTF-8 string: " << output << std::endl; diff --git a/oficonv/include/dcmtk/oficonv/iconv.h b/oficonv/include/dcmtk/oficonv/iconv.h index 931faa2b..14a4842b 100644 --- a/oficonv/include/dcmtk/oficonv/iconv.h +++ b/oficonv/include/dcmtk/oficonv/iconv.h @@ -32,9 +32,7 @@ #include "dcmtk/config/osconfig.h" #include "dcmtk/oficonv/oidefine.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif #include #include @@ -295,10 +293,11 @@ DCMTK_OFICONV_EXPORT void OFiconvlist(int (*do_one) (unsigned int count, const c /** resolve the character encoding name specified by the name argument * to its canonical form. + * The resolved name is returned in a newly allocated buffer that must be freed by the caller using free(). * @param name encoding name * @return canonical encoding name, NULL if unknown */ -DCMTK_OFICONV_EXPORT const char *OFiconv_canonicalize(const char *name); +DCMTK_OFICONV_EXPORT char *OFiconv_canonicalize(const char *name); /** This function can retrieve or set specific conversion setting from the * cd conversion descriptor. The request parameter specifies the operation @@ -310,6 +309,12 @@ DCMTK_OFICONV_EXPORT const char *OFiconv_canonicalize(const char *name); */ DCMTK_OFICONV_EXPORT int OFiconvctl(iconv_t cd, int request, void *argument); +/** This function define a runtime path where to look for the oficonv db files. + * This path is used only after the env variable DCMICONVPATH. + * @param path to the location of esdb folder + */ +DCMTK_OFICONV_EXPORT void OFiconv_setpath(const char *iconv_path); + /// space holder type for OFlocale_charset(). typedef struct { char spaceholder[20]; diff --git a/oficonv/libsrc/Makefile.dep b/oficonv/libsrc/Makefile.dep index d85547d3..66be50c9 100644 --- a/oficonv/libsrc/Makefile.dep +++ b/oficonv/libsrc/Makefile.dep @@ -1,5 +1,6 @@ citrus_bcs.o: citrus_bcs.c ../../config/include/dcmtk/config/osconfig.h \ - citrus_bcs.h + ../include/dcmtk/oficonv/iconv.h ../include/dcmtk/oficonv/oidefine.h \ + ../../ofstd/include/dcmtk/ofstd/ofexport.h citrus_bcs.h citrus_bcs_strtol.o: citrus_bcs_strtol.c \ ../../config/include/dcmtk/config/osconfig.h citrus_bcs.h \ citrus_strtol.h @@ -246,7 +247,7 @@ oficonv_iconv.o: oficonv_iconv.c \ oficonv_logger.o: oficonv_logger.c \ ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/oficonv/iconv.h ../include/dcmtk/oficonv/oidefine.h \ - ../../ofstd/include/dcmtk/ofstd/ofexport.h + ../../ofstd/include/dcmtk/ofstd/ofexport.h citrus_lock.h oficonv_strcasestr.o: oficonv_strcasestr.c \ ../../config/include/dcmtk/config/osconfig.h oficonv_strcasestr.h oficonv_strlcpy.o: oficonv_strlcpy.c \ diff --git a/oficonv/libsrc/citrus_bcs.c b/oficonv/libsrc/citrus_bcs.c index d514dcf7..3010d0e8 100644 --- a/oficonv/libsrc/citrus_bcs.c +++ b/oficonv/libsrc/citrus_bcs.c @@ -25,12 +25,25 @@ */ #include "dcmtk/config/osconfig.h" +#include "dcmtk/oficonv/iconv.h" #include "citrus_bcs.h" #include #include #include +char *oficonv_path = NULL; + +void OFiconv_setpath(const char *runtime_path) { + if (oficonv_path) { + free(oficonv_path); + oficonv_path = NULL; + } + if (runtime_path) { + oficonv_path = strdup(runtime_path); + } +} + /* * case insensitive comparison between two C strings. */ @@ -184,6 +197,9 @@ void get_data_path(char *path_out, size_t path_size, const char *dirname, const // retrieve the DCMICONVPATH environment variable env = getenv(OFICONV_PATH_VARIABLE); + // if the environment variable is not set, use the runtime oficonv path instead: + if ( env == NULL && oficonv_path != NULL ) env = oficonv_path; + // if the environment variable is not set, use DEFAULT_SUPPORT_DATA_DIR instead if (env == NULL) env = DEFAULT_SUPPORT_DATA_DIR; diff --git a/oficonv/libsrc/citrus_bcs.h b/oficonv/libsrc/citrus_bcs.h index 54e347eb..ac8d3f06 100644 --- a/oficonv/libsrc/citrus_bcs.h +++ b/oficonv/libsrc/citrus_bcs.h @@ -29,9 +29,7 @@ #include "dcmtk/config/osconfig.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_WINDOWS_H #include #endif diff --git a/oficonv/libsrc/citrus_big5.c b/oficonv/libsrc/citrus_big5.c index d53a0371..ec527743 100644 --- a/oficonv/libsrc/citrus_big5.c +++ b/oficonv/libsrc/citrus_big5.c @@ -65,11 +65,7 @@ #include "dcmtk/oficonv/queue.h" #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_csmapper.c b/oficonv/libsrc/citrus_csmapper.c index 67cf247b..57294b72 100644 --- a/oficonv/libsrc/citrus_csmapper.c +++ b/oficonv/libsrc/citrus_csmapper.c @@ -28,9 +28,7 @@ #include "citrus_csmapper.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_QUEUE_H #include #else diff --git a/oficonv/libsrc/citrus_db.c b/oficonv/libsrc/citrus_db.c index e8c649c4..2c4b2b54 100644 --- a/oficonv/libsrc/citrus_db.c +++ b/oficonv/libsrc/citrus_db.c @@ -27,11 +27,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_db.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_db_factory.c b/oficonv/libsrc/citrus_db_factory.c index 6f4a23f0..11e7ea22 100644 --- a/oficonv/libsrc/citrus_db_factory.c +++ b/oficonv/libsrc/citrus_db_factory.c @@ -27,9 +27,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_db_factory.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_QUEUE_H #include #else diff --git a/oficonv/libsrc/citrus_db_hash.c b/oficonv/libsrc/citrus_db_hash.c index 92c06202..4d41a941 100644 --- a/oficonv/libsrc/citrus_db_hash.c +++ b/oficonv/libsrc/citrus_db_hash.c @@ -27,11 +27,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_db_hash.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include diff --git a/oficonv/libsrc/citrus_dechanyu.c b/oficonv/libsrc/citrus_dechanyu.c index 9770b10b..bfe53a98 100644 --- a/oficonv/libsrc/citrus_dechanyu.c +++ b/oficonv/libsrc/citrus_dechanyu.c @@ -27,10 +27,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_dechanyu.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - #ifndef __CONCAT #define __CONCAT(x,y) x ## y #endif diff --git a/oficonv/libsrc/citrus_esdb.c b/oficonv/libsrc/citrus_esdb.c index 5de0eaed..c87b33fc 100644 --- a/oficonv/libsrc/citrus_esdb.c +++ b/oficonv/libsrc/citrus_esdb.c @@ -27,11 +27,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_esdb.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_euc.c b/oficonv/libsrc/citrus_euc.c index 53d29280..ce0a51cd 100644 --- a/oficonv/libsrc/citrus_euc.c +++ b/oficonv/libsrc/citrus_euc.c @@ -59,11 +59,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_euc.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_euctw.c b/oficonv/libsrc/citrus_euctw.c index c2cadfcb..2a1d36d1 100644 --- a/oficonv/libsrc/citrus_euctw.c +++ b/oficonv/libsrc/citrus_euctw.c @@ -54,11 +54,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_euctw.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_gbk2k.c b/oficonv/libsrc/citrus_gbk2k.c index 9b7a34f0..c0c6b215 100644 --- a/oficonv/libsrc/citrus_gbk2k.c +++ b/oficonv/libsrc/citrus_gbk2k.c @@ -27,11 +27,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_gbk2k.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_hash.c b/oficonv/libsrc/citrus_hash.c index 6216f48a..3dbcffd4 100644 --- a/oficonv/libsrc/citrus_hash.c +++ b/oficonv/libsrc/citrus_hash.c @@ -27,11 +27,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_hash.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include diff --git a/oficonv/libsrc/citrus_hz.c b/oficonv/libsrc/citrus_hz.c index 4bf17533..3c66d6a3 100644 --- a/oficonv/libsrc/citrus_hz.c +++ b/oficonv/libsrc/citrus_hz.c @@ -28,9 +28,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_hz.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_QUEUE_H #include #else diff --git a/oficonv/libsrc/citrus_iconv.c b/oficonv/libsrc/citrus_iconv.c index 193fc75a..0b72f792 100644 --- a/oficonv/libsrc/citrus_iconv.c +++ b/oficonv/libsrc/citrus_iconv.c @@ -27,9 +27,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_iconv.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_QUEUE_H #include #else @@ -134,7 +132,7 @@ open_shared(struct _citrus_iconv_shared * * rci, int ret; #ifdef DCMTK_ENABLE_ICONV_PASSTHROUGH - /* + /* * Use a pass-through when the (src,dest) encodings are the same. */ module = (strcmp(src, dst) != 0) ? "iconv_std" : "iconv_none"; @@ -387,7 +385,7 @@ _citrus_iconv_close_nofree(struct _citrus_iconv *cv) } } -const char +char *_citrus_iconv_canonicalize(const char *name) { char *buf; diff --git a/oficonv/libsrc/citrus_iconv.h b/oficonv/libsrc/citrus_iconv.h index 13081e1c..1a871444 100644 --- a/oficonv/libsrc/citrus_iconv.h +++ b/oficonv/libsrc/citrus_iconv.h @@ -35,11 +35,10 @@ struct _citrus_iconv_ops; struct _citrus_iconv; BEGIN_EXTERN_C -int _citrus_iconv_open(struct _citrus_iconv * * , - const char * , const char * ); -void _citrus_iconv_close(struct _citrus_iconv *); -void _citrus_iconv_close_nofree(struct _citrus_iconv *); -const char *_citrus_iconv_canonicalize(const char *); +int _citrus_iconv_open(struct _citrus_iconv * * , const char * , const char * ); +void _citrus_iconv_close(struct _citrus_iconv *); +void _citrus_iconv_close_nofree(struct _citrus_iconv *); +char *_citrus_iconv_canonicalize(const char *); END_EXTERN_C diff --git a/oficonv/libsrc/citrus_iconv_local.h b/oficonv/libsrc/citrus_iconv_local.h index 9afdbce6..1e500b12 100644 --- a/oficonv/libsrc/citrus_iconv_local.h +++ b/oficonv/libsrc/citrus_iconv_local.h @@ -110,14 +110,14 @@ struct _citrus_iconv_shared { _citrus_module_t ci_module; unsigned int ci_used_count; char *ci_convname; - bool ci_discard_ilseq; - struct iconv_hooks *ci_hooks; - bool ci_ilseq_invalid; }; struct _citrus_iconv { struct _citrus_iconv_shared *cv_shared; void *cv_closure; + struct iconv_hooks *ci_hooks; + bool ci_discard_ilseq; + bool ci_ilseq_invalid; }; #endif diff --git a/oficonv/libsrc/citrus_iconv_none.c b/oficonv/libsrc/citrus_iconv_none.c index ee1050d4..6974ce5d 100644 --- a/oficonv/libsrc/citrus_iconv_none.c +++ b/oficonv/libsrc/citrus_iconv_none.c @@ -120,9 +120,9 @@ _citrus_iconv_none_iconv_convert(struct _citrus_iconv * ci , len = *outbytes; } memcpy(*out, *in, len); - in += len; + *in += len; *inbytes -= len; - out += len; + *out += len; *outbytes -= len; *invalids = 0; if (e2big) diff --git a/oficonv/libsrc/citrus_iconv_std.c b/oficonv/libsrc/citrus_iconv_std.c index 7b984a54..aef71e0a 100644 --- a/oficonv/libsrc/citrus_iconv_std.c +++ b/oficonv/libsrc/citrus_iconv_std.c @@ -522,10 +522,10 @@ _citrus_iconv_std_iconv_convert(struct _citrus_iconv * cv, tmpin = *in; szrin = szrout = 0; ret = mbtocsx(&sc->sc_src_encoding, &csid, &idx, &tmpin, - *inbytes, &szrin, cv->cv_shared->ci_hooks); + *inbytes, &szrin, cv->ci_hooks); if (ret != 0 && (ret != EILSEQ || - !cv->cv_shared->ci_discard_ilseq)) { + !cv->ci_discard_ilseq)) { goto err; } else if (ret == EILSEQ) { /* @@ -565,18 +565,18 @@ _citrus_iconv_std_iconv_convert(struct _citrus_iconv * cv, * Some software depends on this behavior * though this is against POSIX specification. */ - if (cv->cv_shared->ci_ilseq_invalid != 0) { + if (cv->ci_ilseq_invalid != 0) { ret = EILSEQ; goto err; } inval++; szrout = 0; if ((((flags & _CITRUS_ICONV_F_HIDE_INVALID) == 0) && - !cv->cv_shared->ci_discard_ilseq) && + !cv->ci_discard_ilseq) && is->is_use_invalid) { ret = wctombx(&sc->sc_dst_encoding, *out, *outbytes, is->is_invalid, - &szrout, cv->cv_shared->ci_hooks); + &szrout, cv->ci_hooks); if (ret) goto err; } @@ -587,7 +587,7 @@ _citrus_iconv_std_iconv_convert(struct _citrus_iconv * cv, /* csid/index -> mb */ ret = cstombx(&sc->sc_dst_encoding, *out, *outbytes, csid, idx, &szrout, - cv->cv_shared->ci_hooks); + cv->ci_hooks); if (ret) goto err; next: diff --git a/oficonv/libsrc/citrus_iso2022.c b/oficonv/libsrc/citrus_iso2022.c index 31e6ca10..5e44de86 100644 --- a/oficonv/libsrc/citrus_iso2022.c +++ b/oficonv/libsrc/citrus_iso2022.c @@ -28,11 +28,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_iso2022.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_jisx0208.c b/oficonv/libsrc/citrus_jisx0208.c index cd1b300b..0bca956a 100644 --- a/oficonv/libsrc/citrus_jisx0208.c +++ b/oficonv/libsrc/citrus_jisx0208.c @@ -59,11 +59,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_euc.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_johab.c b/oficonv/libsrc/citrus_johab.c index 653e11af..ac40c752 100644 --- a/oficonv/libsrc/citrus_johab.c +++ b/oficonv/libsrc/citrus_johab.c @@ -27,11 +27,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_johab.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_lock.h b/oficonv/libsrc/citrus_lock.h index 05fdd01f..30b8d8bf 100644 --- a/oficonv/libsrc/citrus_lock.h +++ b/oficonv/libsrc/citrus_lock.h @@ -34,16 +34,20 @@ #ifdef HAVE_WINDOWS_H #include -#define WLOCK(lock) AcquireSRWLockExclusive(lock); -#define UNLOCK(lock) ReleaseSRWLockExclusive(lock); +#define WLOCK(lock) AcquireSRWLockExclusive(lock); +#define UNLOCK(lock) ReleaseSRWLockExclusive(lock); +#define RLOCK(lock) AcquireSRWLockShared(lock); +#define UNRLOCK(lock) ReleaseSRWLockShared(lock); #else /* HAVE_WINDOWS_H */ #ifdef HAVE_PTHREAD_H #include -#define WLOCK(lock) pthread_rwlock_wrlock(lock); -#define UNLOCK(lock) pthread_rwlock_unlock(lock); +#define WLOCK(lock) pthread_rwlock_wrlock(lock); +#define UNLOCK(lock) pthread_rwlock_unlock(lock); +#define RLOCK(lock) pthread_rwlock_rdlock(lock); +#define UNRLOCK(lock) pthread_rwlock_unlock(lock); #else /* HAVE_PTHREAD_H */ @@ -55,8 +59,10 @@ #else /* WITH_THREADS */ -#define WLOCK(lock) /* nothing */ ; -#define UNLOCK(lock) /* nothing */ ; +#define WLOCK(lock) /* nothing */ ; +#define UNLOCK(lock) /* nothing */ ; +#define RLOCK(lock) /* nothing */ ; +#define UNRLOCK(lock) /* nothing */ ; #endif /* WITH_THREADS */ diff --git a/oficonv/libsrc/citrus_lookup.c b/oficonv/libsrc/citrus_lookup.c index 51d95166..4f7fbe61 100644 --- a/oficonv/libsrc/citrus_lookup.c +++ b/oficonv/libsrc/citrus_lookup.c @@ -27,11 +27,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_lookup.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #ifdef HAVE_DIRENT_H #include #endif diff --git a/oficonv/libsrc/citrus_mapper.c b/oficonv/libsrc/citrus_mapper.c index 9c0fa72b..eb1769a9 100644 --- a/oficonv/libsrc/citrus_mapper.c +++ b/oficonv/libsrc/citrus_mapper.c @@ -27,12 +27,8 @@ #include "dcmtk/config/osconfig.h" #include "citrus_mapper.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif -#ifdef HAVE_SYS_STAT_H #include -#endif #ifdef HAVE_SYS_QUEUE_H #include @@ -197,14 +193,17 @@ lookup_mapper_entry(const char *dir, const char *mapname, void *linebuf, /* get module name */ *module = p; cq = _citrus_bcs_skip_nonws_len(cp, &len); - strlcpy(p, cp, (size_t)(cq - cp + 1)); + /* we cannot use strlcpy here because cp is not guaranteed to be null terminated */ + strncpy(p, cp, (size_t)(cq - cp)); + *(p + (cq - cp)) = '\0'; p += cq - cp + 1; /* get variable */ *variable = p; cp = _citrus_bcs_skip_ws_len(cq, &len); - strlcpy(p, cp, len + 1); - + /* we cannot use strlcpy here because cp is not guaranteed to be null terminated */ + strncpy(p, cp, len); + *(p+len) = '\0'; ret = 0; quit: diff --git a/oficonv/libsrc/citrus_mapper_zone.c b/oficonv/libsrc/citrus_mapper_zone.c index 0b42f626..be245cbf 100644 --- a/oficonv/libsrc/citrus_mapper_zone.c +++ b/oficonv/libsrc/citrus_mapper_zone.c @@ -338,7 +338,8 @@ static void /*ARGSUSED*/ _citrus_mapper_zone_mapper_uninit(struct _citrus_csmapper *cm ) { - (void) cm; + if (cm && cm->cm_closure) + free(cm->cm_closure); } static int diff --git a/oficonv/libsrc/citrus_mmap.c b/oficonv/libsrc/citrus_mmap.c index 826c5bed..2be75429 100644 --- a/oficonv/libsrc/citrus_mmap.c +++ b/oficonv/libsrc/citrus_mmap.c @@ -31,17 +31,11 @@ #ifdef HAVE_SYS_MMAN_H #include #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif -#ifdef HAVE_SYS_STAT_H #include -#endif #include -#ifdef HAVE_FCNTL_H #include /* for O_RDONLY, O_CLOEXEC */ -#endif #include #include #include diff --git a/oficonv/libsrc/citrus_module.c b/oficonv/libsrc/citrus_module.c index e8ae9db5..0454d84c 100644 --- a/oficonv/libsrc/citrus_module.c +++ b/oficonv/libsrc/citrus_module.c @@ -88,11 +88,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_module.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #ifdef HAVE_DIRENT_H #include #endif diff --git a/oficonv/libsrc/citrus_mskanji.c b/oficonv/libsrc/citrus_mskanji.c index 7aedd9ee..7349f951 100644 --- a/oficonv/libsrc/citrus_mskanji.c +++ b/oficonv/libsrc/citrus_mskanji.c @@ -60,11 +60,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_mskanji.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_none.c b/oficonv/libsrc/citrus_none.c index 2fdb254b..ae2f5a0d 100644 --- a/oficonv/libsrc/citrus_none.c +++ b/oficonv/libsrc/citrus_none.c @@ -30,11 +30,7 @@ #include "dcmtk/oficonv/iconv.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_region.h b/oficonv/libsrc/citrus_region.h index 4254690e..1e5a5119 100644 --- a/oficonv/libsrc/citrus_region.h +++ b/oficonv/libsrc/citrus_region.h @@ -32,10 +32,7 @@ #include #include #include - -#ifdef HAVE_SYS_TYPES_H #include -#endif struct _citrus_region { diff --git a/oficonv/libsrc/citrus_types.h b/oficonv/libsrc/citrus_types.h index afcc0888..3ad1461a 100644 --- a/oficonv/libsrc/citrus_types.h +++ b/oficonv/libsrc/citrus_types.h @@ -29,9 +29,7 @@ #include "dcmtk/config/osconfig.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif #include #ifndef OFICONV_CITRUS_WC_T_DEFINED diff --git a/oficonv/libsrc/citrus_utf1632.c b/oficonv/libsrc/citrus_utf1632.c index cb53ff79..c1214ea5 100644 --- a/oficonv/libsrc/citrus_utf1632.c +++ b/oficonv/libsrc/citrus_utf1632.c @@ -27,11 +27,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_utf1632.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_utf8.c b/oficonv/libsrc/citrus_utf8.c index 09abce6a..c7be99b0 100644 --- a/oficonv/libsrc/citrus_utf8.c +++ b/oficonv/libsrc/citrus_utf8.c @@ -59,11 +59,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_utf8.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_viqr.c b/oficonv/libsrc/citrus_viqr.c index e3c5b20a..9b1939ff 100644 --- a/oficonv/libsrc/citrus_viqr.c +++ b/oficonv/libsrc/citrus_viqr.c @@ -42,11 +42,7 @@ (var) = (tvar)) #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/citrus_zw.c b/oficonv/libsrc/citrus_zw.c index c8141d87..771c6774 100644 --- a/oficonv/libsrc/citrus_zw.c +++ b/oficonv/libsrc/citrus_zw.c @@ -28,11 +28,7 @@ #include "dcmtk/config/osconfig.h" #include "citrus_zw.h" -#ifdef HAVE_SYS_TYPES_H #include -#endif - - #include #include #include diff --git a/oficonv/libsrc/oficonv_iconv.c b/oficonv/libsrc/oficonv_iconv.c index c7a625ea..a20cfe9b 100644 --- a/oficonv/libsrc/oficonv_iconv.c +++ b/oficonv/libsrc/oficonv_iconv.c @@ -34,10 +34,7 @@ #include "dcmtk/oficonv/queue.h" #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif - #include #include #include @@ -113,9 +110,9 @@ _iconv_open(const char *out, const char *in, struct _citrus_iconv *handle) return ((iconv_t)-1); } - handle->cv_shared->ci_discard_ilseq = strcasestr(out, "//IGNORE"); - handle->cv_shared->ci_ilseq_invalid = false; - handle->cv_shared->ci_hooks = NULL; + handle->ci_discard_ilseq = strcasestr(out, "//IGNORE"); + handle->ci_ilseq_invalid = false; + handle->ci_hooks = NULL; return ((iconv_t)(void *)handle); } @@ -302,10 +299,8 @@ out: OF__iconv_free_list(list, sz); } -const char -*OFiconv_canonicalize(const char *name) +char *OFiconv_canonicalize(const char *name) { - return (_citrus_iconv_canonicalize(name)); } @@ -342,22 +337,22 @@ OFiconvctl(iconv_t cd, int request, void *argument) case ICONV_SET_TRANSLITERATE: return ((*i == 1) ? 0 : -1); case ICONV_GET_DISCARD_ILSEQ: - *i = cv->cv_shared->ci_discard_ilseq ? 1 : 0; + *i = cv->ci_discard_ilseq ? 1 : 0; return (0); case ICONV_SET_DISCARD_ILSEQ: - cv->cv_shared->ci_discard_ilseq = *i; + cv->ci_discard_ilseq = *i; return (0); case ICONV_SET_HOOKS: - cv->cv_shared->ci_hooks = hooks; + cv->ci_hooks = hooks; return (0); case ICONV_SET_FALLBACKS: errno = EOPNOTSUPP; return (-1); case ICONV_GET_ILSEQ_INVALID: - *i = cv->cv_shared->ci_ilseq_invalid ? 1 : 0; + *i = cv->ci_ilseq_invalid ? 1 : 0; return (0); case ICONV_SET_ILSEQ_INVALID: - cv->cv_shared->ci_ilseq_invalid = *i; + cv->ci_ilseq_invalid = *i; return (0); default: errno = EINVAL; @@ -383,4 +378,5 @@ const char *OFlocale_charset(iconv_locale_allocation_t *buf) void OFiconv_cleanup() { _citrus_csmapper_free(); + OFiconv_setpath(NULL); } diff --git a/oficonv/libsrc/oficonv_logger.c b/oficonv/libsrc/oficonv_logger.c index 839e6870..f29385bf 100644 --- a/oficonv/libsrc/oficonv_logger.c +++ b/oficonv/libsrc/oficonv_logger.c @@ -21,19 +21,33 @@ #include "dcmtk/config/osconfig.h" #include "dcmtk/oficonv/iconv.h" +#include "citrus_lock.h" #include +#ifdef WITH_THREADS +#ifdef HAVE_WINDOWS_H +static SRWLOCK logger_lock = SRWLOCK_INIT; +#elif defined(HAVE_PTHREAD_H) +static pthread_rwlock_t logger_lock = PTHREAD_RWLOCK_INITIALIZER; +#endif +#endif + static oficonv_logger_callback_t logger_callback = NULL; static int log_level = 3; void set_oficonv_logger_callback(oficonv_logger_callback_t callback) { + WLOCK(&logger_lock); logger_callback = callback; + UNLOCK(&logger_lock); } oficonv_logger_callback_t get_oficonv_logger_callback() { - return logger_callback; + RLOCK(&logger_lock); + oficonv_logger_callback_t result = logger_callback; + UNRLOCK(&logger_lock); + return result; } void set_oficonv_log_level(int level) @@ -43,6 +57,7 @@ void set_oficonv_log_level(int level) void oficonv_log(int level, const char *text1, const char *text2, const char *text3) { + RLOCK(&logger_lock); if (logger_callback) logger_callback(level, text1, text2, text3); else { @@ -70,4 +85,5 @@ void oficonv_log(int level, const char *text1, const char *text2, const char *te } if (level >= log_level) fprintf(stderr, "%s %s%s%s\n", level_text, text1, text2, text3); } + UNRLOCK(&logger_lock); } diff --git a/oficonv/tests/tests.cc b/oficonv/tests/tests.cc index 968149c8..81c38ce5 100644 --- a/oficonv/tests/tests.cc +++ b/oficonv/tests/tests.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2022, OFFIS e.V. + * Copyright (C) 2022-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -109,4 +109,7 @@ OFTEST_REGISTER(oficonv__iconv); OFTEST_REGISTER(oflocale_charset); OFTEST_REGISTER(oficonvctl); +// finally clean up memory +OFTEST_REGISTER(oficonv_cleanup); + OFTEST_MAIN("oficonv") diff --git a/oficonv/tests/ticonv.cc b/oficonv/tests/ticonv.cc index ad12fd02..6a46eab2 100644 --- a/oficonv/tests/ticonv.cc +++ b/oficonv/tests/ticonv.cc @@ -187,7 +187,7 @@ OFTEST(oficonv_open) OFTEST(oficonv_canonicalize) { - const char *c; + char *c; // these are the canonical names we expect as result OFString latin1("ISO-8859-1"); @@ -213,46 +213,46 @@ OFTEST(oficonv_canonicalize) // exercise all synonyms for ISO-8859-1 as defined in oficonv/datasrc/esdb/esdb.alias, // with mixed uppercase/lowercase spellings - c = OFiconv_canonicalize("cp819"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("csisolatin1"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("ibm819"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("iso_8859-1"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("iso_8859-1:1987"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("iso-ir-100"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("iso8859-1"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("l1"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("latin1"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("CP819"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("CSISOLATIN1"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("IBM819"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("ISO_8859-1"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("ISO_8859-1:1987"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("ISO-IR-100"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("ISO8859-1"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("L1"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("LATIN1"); OFCHECK(c && latin1 == c); - c = OFiconv_canonicalize("cSiSoLaTiN1"); OFCHECK(c && latin1 == c); + c = OFiconv_canonicalize("cp819"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("csisolatin1"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("ibm819"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("iso_8859-1"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("iso_8859-1:1987"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("iso-ir-100"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("iso8859-1"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("l1"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("latin1"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("CP819"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("CSISOLATIN1"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("IBM819"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("ISO_8859-1"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("ISO_8859-1:1987"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("ISO-IR-100"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("ISO8859-1"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("L1"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("LATIN1"); OFCHECK(c && latin1 == c); if (c) free(c); + c = OFiconv_canonicalize("cSiSoLaTiN1"); OFCHECK(c && latin1 == c); if (c) free(c); // exercise at least one synonym for each supported character set - c = OFiconv_canonicalize("gb2312"); OFCHECK(c && euc_cn == c); - c = OFiconv_canonicalize("iso-ir-149"); OFCHECK(c && euc_kr == c); - c = OFiconv_canonicalize("iso-ir-101"); OFCHECK(c && latin2 == c); - c = OFiconv_canonicalize("latin3"); OFCHECK(c && latin3 == c); - c = OFiconv_canonicalize("iso_8859-4"); OFCHECK(c && latin4 == c); - c = OFiconv_canonicalize("cyrillic"); OFCHECK(c && cyrillic == c); - c = OFiconv_canonicalize("arabic"); OFCHECK(c && arabic == c); - c = OFiconv_canonicalize("greek"); OFCHECK(c && greek == c); - c = OFiconv_canonicalize("hebrew"); OFCHECK(c && hebrew == c); - c = OFiconv_canonicalize("iso-ir-148"); OFCHECK(c && latin5 == c); - c = OFiconv_canonicalize("tis-620"); OFCHECK(c && thai == c); - c = OFiconv_canonicalize("iso-ir-203"); OFCHECK(c && latin9 == c); - c = OFiconv_canonicalize("ascii"); OFCHECK(c && ascii == c); - c = OFiconv_canonicalize("jis_x0201"); OFCHECK(c && jisx0201 == c); - c = OFiconv_canonicalize("jis0208"); OFCHECK(c && jisx0208 == c); - c = OFiconv_canonicalize("ms_kanji"); OFCHECK(c && shift_jis == c); - c = OFiconv_canonicalize("jis_x0212"); OFCHECK(c && jisx0212 == c); - c = OFiconv_canonicalize("utf8"); OFCHECK(c && utf8 == c); - c = OFiconv_canonicalize("unicode"); OFCHECK(c && utf16 == c); + c = OFiconv_canonicalize("gb2312"); OFCHECK(c && euc_cn == c); if (c) free(c); + c = OFiconv_canonicalize("iso-ir-149"); OFCHECK(c && euc_kr == c); if (c) free(c); + c = OFiconv_canonicalize("iso-ir-101"); OFCHECK(c && latin2 == c); if (c) free(c); + c = OFiconv_canonicalize("latin3"); OFCHECK(c && latin3 == c); if (c) free(c); + c = OFiconv_canonicalize("iso_8859-4"); OFCHECK(c && latin4 == c); if (c) free(c); + c = OFiconv_canonicalize("cyrillic"); OFCHECK(c && cyrillic == c); if (c) free(c); + c = OFiconv_canonicalize("arabic"); OFCHECK(c && arabic == c); if (c) free(c); + c = OFiconv_canonicalize("greek"); OFCHECK(c && greek == c); if (c) free(c); + c = OFiconv_canonicalize("hebrew"); OFCHECK(c && hebrew == c); if (c) free(c); + c = OFiconv_canonicalize("iso-ir-148"); OFCHECK(c && latin5 == c); if (c) free(c); + c = OFiconv_canonicalize("tis-620"); OFCHECK(c && thai == c); if (c) free(c); + c = OFiconv_canonicalize("iso-ir-203"); OFCHECK(c && latin9 == c); if (c) free(c); + c = OFiconv_canonicalize("ascii"); OFCHECK(c && ascii == c); if (c) free(c); + c = OFiconv_canonicalize("jis_x0201"); OFCHECK(c && jisx0201 == c); if (c) free(c); + c = OFiconv_canonicalize("jis0208"); OFCHECK(c && jisx0208 == c); if (c) free(c); + c = OFiconv_canonicalize("ms_kanji"); OFCHECK(c && shift_jis == c); if (c) free(c); + c = OFiconv_canonicalize("jis_x0212"); OFCHECK(c && jisx0212 == c); if (c) free(c); + c = OFiconv_canonicalize("utf8"); OFCHECK(c && utf8 == c); if (c) free(c); + c = OFiconv_canonicalize("unicode"); OFCHECK(c && utf16 == c); if (c) free(c); } @@ -648,3 +648,8 @@ OFTEST(oficonvctl) OFiconv_close(id3); } } + +OFTEST(oficonv_cleanup) +{ + OFiconv_cleanup(); +} diff --git a/oflog/include/dcmtk/oflog/config.h b/oflog/include/dcmtk/oflog/config.h index 38878d70..2e46bb59 100644 --- a/oflog/include/dcmtk/oflog/config.h +++ b/oflog/include/dcmtk/oflog/config.h @@ -93,10 +93,8 @@ #endif #if defined (DCMTK_OFLOG_UNICODE) -# if defined (_MSC_VER) && _MSC_VER >= 1400 +# if defined (_MSC_VER) # define DCMTK_LOG4CPLUS_FSTREAM_ACCEPTS_WCHAR_T -# endif -# if defined (_MSC_VER) && _MSC_VER >= 1600 # define DCMTK_LOG4CPLUS_HAVE_CODECVT_UTF8_FACET # define DCMTK_LOG4CPLUS_HAVE_CODECVT_UTF16_FACET # endif @@ -110,8 +108,7 @@ # define __has_feature(X) 0 #endif -#if (defined (_MSC_VER) && _MSC_VER >= 1600) \ - || defined (__GXX_EXPERIMENTAL_CXX0X__) +#if defined (_MSC_VER) || defined (__GXX_EXPERIMENTAL_CXX0X__) # define DCMTK_LOG4CPLUS_HAVE_modern_cxx_support #endif diff --git a/oflog/include/dcmtk/oflog/config/defines.h b/oflog/include/dcmtk/oflog/config/defines.h index 6f4a46e8..5878555a 100644 --- a/oflog/include/dcmtk/oflog/config/defines.h +++ b/oflog/include/dcmtk/oflog/config/defines.h @@ -32,14 +32,10 @@ #endif /* */ -#ifdef HAVE_SYS_TYPES_H #define DCMTK_LOG4CPLUS_HAVE_SYS_TYPES_H -#endif /* */ -#ifdef HAVE_SYS_STAT_H #define DCMTK_LOG4CPLUS_HAVE_SYS_STAT_H -#endif /* */ #ifdef HAVE_SYS_SYSCALL_H @@ -70,9 +66,7 @@ #endif /* */ -#ifdef HAVE_FCNTL_H #define DCMTK_LOG4CPLUS_HAVE_FCNTL_H -#endif /* */ #define DCMTK_LOG4CPLUS_HAVE_STDARG_H @@ -106,9 +100,7 @@ #endif /* */ -#ifdef HAVE_GETPID #define DCMTK_LOG4CPLUS_HAVE_GETPID -#endif /* */ #ifdef HAVE_PROTOTYPE_GETTIMEOFDAY @@ -181,9 +173,7 @@ #endif /* */ -#ifdef HAVE_STAT #define DCMTK_LOG4CPLUS_HAVE_STAT -#endif /* */ #ifdef WITH_THREADS @@ -260,9 +250,7 @@ #endif /* */ -#ifdef HAVE_VSNPRINTF #define DCMTK_LOG4CPLUS_HAVE_VSNPRINTF -#endif /* */ #ifdef HAVE_PROTOTYPE_STD__VSNPRINTF diff --git a/oflog/include/dcmtk/oflog/config/win32.h b/oflog/include/dcmtk/oflog/config/win32.h index eb8d6e1d..b8e5e0e6 100644 --- a/oflog/include/dcmtk/oflog/config/win32.h +++ b/oflog/include/dcmtk/oflog/config/win32.h @@ -30,8 +30,7 @@ #ifdef _WIN32 -/* This used to be _MSC_VER >= 1400, but MSVC 2005 is broken */ -#if (defined (_MSC_VER) && _MSC_VER > 1400) +#ifdef _MSC_VER # define DCMTK_LOG4CPLUS_HAVE_INTRIN_H #endif @@ -39,7 +38,7 @@ #define DCMTK_LOG4CPLUS_HAVE_TIME_H #define DCMTK_LOG4CPLUS_HAVE_SYS_TIMEB_H #define DCMTK_LOG4CPLUS_HAVE_FTIME -#if defined (_MSC_VER) || defined (__BORLANDC__) +#if defined (_MSC_VER) || defined (HAVE_CLASSIC_BORLAND_COMPILER) #define DCMTK_LOG4CPLUS_HAVE_GMTIME_S #endif @@ -68,9 +67,6 @@ // MSVC has both and so does MinGW. #define DCMTK_LOG4CPLUS_HAVE_VSNPRINTF #define DCMTK_LOG4CPLUS_HAVE__VSNPRINTF -#if _MSC_VER <= 1200 /* Additional settings for VC6 and older */ -#undef DCMTK_LOG4CPLUS_HAVE_VSNPRINTF -#endif // MS secure versions of vprintf(). #ifdef HAVE_VSPRINTF_S @@ -137,13 +133,10 @@ # pragma warning( disable : 4251 ) # define DCMTK_LOG4CPLUS_INLINES_ARE_EXPORTED - -# if _MSC_VER >= 1400 -# define DCMTK_LOG4CPLUS_WORKING_LOCALE -# define DCMTK_LOG4CPLUS_HAVE_FUNCTION_MACRO -# define DCMTK_LOG4CPLUS_HAVE_FUNCSIG_MACRO -# define DCMTK_LOG4CPLUS_HAVE_C99_VARIADIC_MACROS -# endif +# define DCMTK_LOG4CPLUS_WORKING_LOCALE +# define DCMTK_LOG4CPLUS_HAVE_FUNCTION_MACRO +# define DCMTK_LOG4CPLUS_HAVE_FUNCSIG_MACRO +# define DCMTK_LOG4CPLUS_HAVE_C99_VARIADIC_MACROS #endif #if defined (__GNUC__) diff --git a/oflog/libsrc/fileap.cc b/oflog/libsrc/fileap.cc index 7c5a2ee7..41636c19 100644 --- a/oflog/libsrc/fileap.cc +++ b/oflog/libsrc/fileap.cc @@ -35,7 +35,7 @@ #include #include -#if defined (__BORLANDC__) +#if defined (HAVE_CLASSIC_BORLAND_COMPILER) // For _wrename() and _wremove() on Windows. # include #endif @@ -73,7 +73,7 @@ namespace long const DCMTK_LOG4CPLUS_FILE_NOT_FOUND = ENOENT; -static +static long file_rename (tstring const & src, tstring const & target) { @@ -122,8 +122,8 @@ loglog_renaming_result (helpers::LogLog & loglog, tstring const & src, if (ret == 0) { loglog.debug ( - DCMTK_LOG4CPLUS_TEXT("Renamed file ") - + src + DCMTK_LOG4CPLUS_TEXT("Renamed file ") + + src + DCMTK_LOG4CPLUS_TEXT(" to ") + target); } @@ -149,7 +149,7 @@ loglog_opening_result (helpers::LogLog & loglog, if (! os) { loglog.error ( - DCMTK_LOG4CPLUS_TEXT("Failed to open file ") + DCMTK_LOG4CPLUS_TEXT("Failed to open file ") + filename); } } @@ -224,7 +224,7 @@ get_locale_by_name (tstring const & locale_name) // FileAppender ctors and dtor /////////////////////////////////////////////////////////////////////////////// -FileAppender::FileAppender(const tstring& filename_, +FileAppender::FileAppender(const tstring& filename_, STD_NAMESPACE ios_base::openmode mode_, bool immediateFlush_) : immediateFlush(immediateFlush_) , reopenDelay(1) @@ -239,7 +239,7 @@ FileAppender::FileAppender(const tstring& filename_, } -FileAppender::FileAppender(const Properties& props, +FileAppender::FileAppender(const Properties& props, STD_NAMESPACE ios_base::openmode mode_) : Appender(props) , immediateFlush(true) @@ -280,7 +280,7 @@ FileAppender::FileAppender(const Properties& props, void -FileAppender::init(const tstring& filename_, +FileAppender::init(const tstring& filename_, STD_NAMESPACE ios_base::openmode mode_, const log4cplus::tstring& lockFileName_) { @@ -313,7 +313,7 @@ FileAppender::init(const tstring& filename_, imbue (get_locale_by_name (localeName)); if(!out.good()) { - getErrorHandler()->error( DCMTK_LOG4CPLUS_TEXT("Unable to open file: ") + getErrorHandler()->error( DCMTK_LOG4CPLUS_TEXT("Unable to open file: ") + filename); return; } @@ -333,7 +333,7 @@ FileAppender::~FileAppender() // FileAppender public methods /////////////////////////////////////////////////////////////////////////////// -void +void FileAppender::close() { thread::MutexGuard guard (access_mutex); @@ -370,11 +370,11 @@ FileAppender::append(const spi::InternalLoggingEvent& event) { if(!out.good()) { if(!reopen()) { - getErrorHandler()->error( DCMTK_LOG4CPLUS_TEXT("file is not open: ") + getErrorHandler()->error( DCMTK_LOG4CPLUS_TEXT("file is not open: ") + filename); return; } - // Resets the error handler to make it + // Resets the error handler to make it // ready to handle a future append error. else getErrorHandler()->reset(); @@ -522,7 +522,7 @@ RollingFileAppender::rollover(bool alreadyLocked) out.close(); // Reset flags since the C++ standard specified that all the flags // should remain unchanged on a close. - out.clear(); + out.clear(); if (useLockFile) { @@ -573,8 +573,8 @@ RollingFileAppender::rollover(bool alreadyLocked) #endif loglog.debug ( - DCMTK_LOG4CPLUS_TEXT("Renaming file ") - + filename + DCMTK_LOG4CPLUS_TEXT("Renaming file ") + + filename + DCMTK_LOG4CPLUS_TEXT(" to ") + target); ret = file_rename (filename, target); @@ -640,7 +640,7 @@ DailyRollingFileAppender::DailyRollingFileAppender( + properties.getProperty(DCMTK_LOG4CPLUS_TEXT("Schedule"))); theSchedule = DAILY; } - + properties.getInt (maxBackupIndex, DCMTK_LOG4CPLUS_TEXT("MaxBackupIndex")); init(theSchedule); @@ -794,12 +794,12 @@ DailyRollingFileAppender::rollover(bool alreadyLocked) // possible to rename over existing file, e.g. "log.2009-11-07". ret = file_remove (scheduledFilename); #endif - + // Rename filename to scheduledFilename, // e.g. rename "log" to "log.2009-11-07". loglog.debug( DCMTK_LOG4CPLUS_TEXT("Renaming file ") - + filename + + filename + DCMTK_LOG4CPLUS_TEXT(" to ") + scheduledFilename); ret = file_rename (filename, scheduledFilename); @@ -825,7 +825,7 @@ DailyRollingFileAppender::calculateNextRolloverTime(const Time& t) const { switch(schedule) { - case MONTHLY: + case MONTHLY: { struct tm nextMonthTime; t.localtime(&nextMonthTime); diff --git a/oflog/libsrc/log4judp.cc b/oflog/libsrc/log4judp.cc index c79831ea..a1c0b442 100644 --- a/oflog/libsrc/log4judp.cc +++ b/oflog/libsrc/log4judp.cc @@ -50,8 +50,6 @@ is_control (tchar ch) { #if defined (DCMTK_OFLOG_UNICODE) return !! STD_NAMESPACE iswcntrl (STD_NAMESPACE char_traits::to_int_type (ch)); -#elif defined (_MSC_VER) && _MSC_VER <= 1200 /* MSC6 and older */ - return !! iscntrl (STD_NAMESPACE char_traits::to_int_type (ch)); #else return !! STD_NAMESPACE iscntrl (STD_NAMESPACE char_traits::to_int_type (ch)); #endif diff --git a/oflog/libsrc/threads.cc b/oflog/libsrc/threads.cc index 3cb09716..ce56b548 100644 --- a/oflog/libsrc/threads.cc +++ b/oflog/libsrc/threads.cc @@ -90,9 +90,7 @@ yield() #if defined(DCMTK_LOG4CPLUS_USE_PTHREADS) sched_yield(); #elif defined(_WIN32) -#if !defined(_MSC_VER) || _MSC_VER > 1200 /* MSC 6 doesn't know this */ if (! SwitchToThread ()) -#endif Sleep (0); #endif } diff --git a/oflog/libsrc/timehelp.cc b/oflog/libsrc/timehelp.cc index 993f298e..99bf00ec 100644 --- a/oflog/libsrc/timehelp.cc +++ b/oflog/libsrc/timehelp.cc @@ -178,10 +178,10 @@ void Time::gmtime(tm* t) const { time_t clock = tv_sec; -#if defined (DCMTK_LOG4CPLUS_HAVE_GMTIME_S) && defined (_MSC_VER) && _MSC_VER > 1200 - gmtime_s (t, &clock); -#elif defined (DCMTK_LOG4CPLUS_HAVE_GMTIME_S) && defined (__BORLANDC__) +#if defined (DCMTK_LOG4CPLUS_HAVE_GMTIME_S) && defined (HAVE_CLASSIC_BORLAND_COMPILER) gmtime_s (&clock, t); +#elif defined (DCMTK_LOG4CPLUS_HAVE_GMTIME_S) && defined (_MSC_VER) + gmtime_s (t, &clock); #elif defined (DCMTK_LOG4CPLUS_NEED_GMTIME_R) gmtime_r (&clock, t); #else diff --git a/ofstd/include/dcmtk/ofstd/diag/clangprg.def b/ofstd/include/dcmtk/ofstd/diag/clangprg.def new file mode 100644 index 00000000..5f49a4a1 --- /dev/null +++ b/ofstd/include/dcmtk/ofstd/diag/clangprg.def @@ -0,0 +1,7 @@ +/* MacOS 15.5 defines some Clang specific pragmas in header files + * without checking if the current compiler is actually Clang. + * This code allows us to suppress warnings when compiling with GCC + */ +#if defined(__APPLE__) && defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#endif diff --git a/ofstd/include/dcmtk/ofstd/diag/vsconstexp.def b/ofstd/include/dcmtk/ofstd/diag/vsconstexp.def new file mode 100644 index 00000000..5a48c307 --- /dev/null +++ b/ofstd/include/dcmtk/ofstd/diag/vsconstexp.def @@ -0,0 +1,7 @@ +// Visual Studio warns if a certain expression is constant +// i.e. known at compile time and used in a conditional statement. +// In C++17 constexpr should be used then but we don't want a warning +// about it. +#ifdef _MSC_VER +#pragma warning(disable: 4127) +#endif \ No newline at end of file diff --git a/ofstd/include/dcmtk/ofstd/ofchrenc.h b/ofstd/include/dcmtk/ofstd/ofchrenc.h index 80fa13a3..0babe110 100644 --- a/ofstd/include/dcmtk/ofstd/ofchrenc.h +++ b/ofstd/include/dcmtk/ofstd/ofchrenc.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2011-2018, OFFIS e.V. + * Copyright (C) 2011-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -267,7 +267,7 @@ public: * @remark This list of code pages is only available if DCMTK is compiled * on Windows Operating Systems (defining _WIN32) */ - //@{ + ///@{ /// system default Windows ANSI code page. See Windows function GetACP(). /// @remark Only available on Windows Operating Systems (defining _WIN32). @@ -285,7 +285,7 @@ public: /// @remark Only available on Windows Operating Systems (defining _WIN32). static const unsigned int CPC_UTF8; - //@} + ///@} // --- static Windows-specific functions --- diff --git a/ofstd/include/dcmtk/ofstd/ofcmdln.h b/ofstd/include/dcmtk/ofstd/ofcmdln.h index 0b99f7b1..30b5c007 100644 --- a/ofstd/include/dcmtk/ofstd/ofcmdln.h +++ b/ofstd/include/dcmtk/ofstd/ofcmdln.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1998-2021, OFFIS e.V. + * Copyright (C) 1998-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -35,8 +35,8 @@ * macro definition * *--------------------*/ -// Only use wchar_t on windows (not mingw) and don't use it on MSC6 -#if defined(HAVE_WINDOWS_H) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER > 1200) +// Only use wchar_t on windows (not mingw) +#if defined(HAVE_WINDOWS_H) && !defined(__MINGW32__) # define DCMTK_USE_WCHAR_T #endif #if defined(WIDE_CHAR_MAIN_FUNCTION) && defined(DCMTK_USE_WCHAR_T) diff --git a/ofstd/include/dcmtk/ofstd/ofcond.h b/ofstd/include/dcmtk/ofstd/ofcond.h index 63f72bc1..0f9e39a4 100644 --- a/ofstd/include/dcmtk/ofstd/ofcond.h +++ b/ofstd/include/dcmtk/ofstd/ofcond.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2023, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -101,7 +101,8 @@ struct DCMTK_OFSTD_EXPORT OFConditionConst * All constants defined here use module number 0, which is reserved for * global definitions. Other constants are defined in other modules. */ -//@{ +///@{ + /// condition constant: successful completion extern DCMTK_OFSTD_EXPORT const OFConditionConst EC_Normal; /// condition constant: error, function called with illegal parameters @@ -155,7 +156,7 @@ extern DCMTK_OFSTD_EXPORT const OFConditionConst EC_IPCMessageQueueEmpty; /// condition constant: error, IPC message empty (zero length) extern DCMTK_OFSTD_EXPORT const OFConditionConst EC_IPCEmptyMessage; -//@} +///@} /** use this macro for creating static OFCondition instances. Instead of an diff --git a/ofstd/include/dcmtk/ofstd/ofdate.h b/ofstd/include/dcmtk/ofstd/ofdate.h index 87089a67..1f6ad131 100644 --- a/ofstd/include/dcmtk/ofstd/ofdate.h +++ b/ofstd/include/dcmtk/ofstd/ofdate.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2017, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -26,9 +26,7 @@ #include "dcmtk/config/osconfig.h" BEGIN_EXTERN_C -#ifdef HAVE_SYS_TYPES_H -# include /* for struct time_t */ -#endif +#include /* for struct time_t */ END_EXTERN_C #include "dcmtk/ofstd/ofstring.h" /* for class OFString */ diff --git a/ofstd/include/dcmtk/ofstd/ofdiag.h b/ofstd/include/dcmtk/ofstd/ofdiag.h index 0590a385..9fd9e675 100644 --- a/ofstd/include/dcmtk/ofstd/ofdiag.h +++ b/ofstd/include/dcmtk/ofstd/ofdiag.h @@ -274,6 +274,8 @@ #define DCMTK_DIAGNOSTIC_IGNORE_USE_AFTER_FREE "dcmtk/ofstd/diag/useafree.def" #define DCMTK_DIAGNOSTIC_IGNORE_ARRAY_BOUNDS "dcmtk/ofstd/diag/arrybnds.def" #define DCMTK_DIAGNOSTIC_IGNORE_UNSIGNED_UNARY_MINUS "dcmtk/ofstd/diag/unarymin.def" +#define DCMTK_DIAGNOSTIC_IGNORE_CLANG_PRAGMAS_ON_GCC "dcmtk/ofstd/diag/clangprg.def" +#define DCMTK_DIAGNOSTIC_IGNORE_VISUAL_STUDIO_CONSTANT_EXPRESSION_WARNING "dcmtk/ofstd/diag/vsconstexp.def" // readable shorthands for compiler version checks #define DCMTK_DIAGNOSTIC_MIN_GCC_VERSION(MAJOR, MINOR, PATCH)\ diff --git a/ofstd/include/dcmtk/ofstd/offile.h b/ofstd/include/dcmtk/ofstd/offile.h index f64ad548..4ddb768f 100644 --- a/ofstd/include/dcmtk/ofstd/offile.h +++ b/ofstd/include/dcmtk/ofstd/offile.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2006-2024, OFFIS e.V. + * Copyright (C) 2006-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -33,9 +33,7 @@ #include BEGIN_EXTERN_C -#ifdef HAVE_SYS_STAT_H #include /* needed for struct _stati64 on Win32 */ -#endif END_EXTERN_C #ifdef HAVE_UNIX_H @@ -74,24 +72,17 @@ typedef fpos_t offile_fpos_t; // Use POSIX 64 bit file offset type when available #ifdef HAVE_OFF64_T typedef off64_t offile_off_t; -#elif !defined(OF_NO_SINT64) // Otherwise use a 64 bit integer +#else +// Otherwise use a 64 bit integer typedef Sint64 offile_off_t; -#else // Cry when 64 LFS is required but no 64 bit integer exists -#error \ - Could not find a suitable offset-type for LFS64 support. #endif #else // Implicit LFS or no LFS #if defined(DCMTK_ENABLE_LFS) && DCMTK_ENABLE_LFS == DCMTK_LFS #if defined(SIZEOF_FPOS_T) && (!defined(SIZEOF_OFF_T) || SIZEOF_FPOS_T > SIZEOF_OFF_T) -// strange Windows LFS where sizeof(fpos_t) == 8 but sizeof(off_t) == 4 -#ifndef OF_NO_SINT64 // Use a 64 bit integer +// strange Windows LFS where sizeof(fpos_t) == 8 but sizeof(off_t) == 4. Use a 64 bit integer typedef Sint64 offile_off_t; -#else // Cry when LFS is required but no 64 bit integer exists -#error \ - Could not find a suitable offset-type for LFS support. -#endif #else typedef off_t offile_off_t; #endif diff --git a/ofstd/include/dcmtk/ofstd/ofjsmn.h b/ofstd/include/dcmtk/ofstd/ofjsmn.h new file mode 100644 index 00000000..392a4830 --- /dev/null +++ b/ofstd/include/dcmtk/ofstd/ofjsmn.h @@ -0,0 +1,473 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef OFJSMN_H +#define OFJSMN_H + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/ofstd/oftypes.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern DCMTK_OFSTD_EXPORT +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1 << 0, + JSMN_ARRAY = 1 << 1, + JSMN_STRING = 1 << 2, + JSMN_PRIMITIVE = 1 << 3 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct jsmntok { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct jsmn_parser { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + /* Skip starting quote */ + parser->pos++; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* OFJSMN_H */ diff --git a/ofstd/include/dcmtk/ofstd/oflist.h b/ofstd/include/dcmtk/ofstd/oflist.h index e26b8dc8..b1135ec2 100644 --- a/ofstd/include/dcmtk/ofstd/oflist.h +++ b/ofstd/include/dcmtk/ofstd/oflist.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1997-2024, OFFIS e.V. + * Copyright (C) 1997-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -51,16 +51,9 @@ // of one class of the STL #include -#ifdef HAVE_STD_NAMESPACE #define OFList std::list #define OFListIterator(x) std::list< x >::iterator #define OFListConstIterator(x) std::list< x >::const_iterator -#else -#define OFList list -#define OFListIterator(x) list< x >::iterator -#define OFListConstIterator(x) list< x >::const_iterator -#endif - #define OFListInsert(InputIterator, T, c, pos, first, last) (c).insert((pos), (first), (last)) #define OFListRemoveIf(Predicate, T, c, pred) (c).remove_if((pred)) @@ -79,10 +72,8 @@ #define OFLIST_TYPENAME BEGIN_EXTERN_C -#ifdef HAVE_SYS_TYPES_H /* needed e.g. on Solaris for definition of size_t */ #include -#endif END_EXTERN_C // OFListLinkBase, OFListLink and OFListBase are classes for internal diff --git a/ofstd/include/dcmtk/ofstd/ofmath.h b/ofstd/include/dcmtk/ofstd/ofmath.h index 288503ad..6b50b3de 100644 --- a/ofstd/include/dcmtk/ofstd/ofmath.h +++ b/ofstd/include/dcmtk/ofstd/ofmath.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015, OFFIS e.V. + * Copyright (C) 2015-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -42,6 +42,7 @@ public: static OFBool (isinf) (float f); static OFBool (isinf) (double d); + static double (sqrt) (double d); template static inline OFTypename OFenable_if::value,OFBool>::type (isinf) ( const Integer i ) { return (isinf) ( OFstatic_cast( double, i ) ); } @@ -77,6 +78,12 @@ public: */ static OFBool isinf( double d ); + /** Computes the square root of a floating point number. + * @param d the floating point value to inspect. + * @return the square root of d. + */ + static double sqrt( double d ); + /** Casts the argument to double and calls OFMath::isinf(double) on the result. * @param i an integer, i.e. OFis_integral::value equals OFTrue. * @return OFMath::isinf(OFstatic_cast(double,i)). diff --git a/ofstd/include/dcmtk/ofstd/ofmem.h b/ofstd/include/dcmtk/ofstd/ofmem.h index d7a4e96d..3559e201 100644 --- a/ofstd/include/dcmtk/ofstd/ofmem.h +++ b/ofstd/include/dcmtk/ofstd/ofmem.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2012-2017, OFFIS e.V. + * Copyright (C) 2012-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -40,11 +40,7 @@ using OFunique_ptr = std::unique_ptr; #if defined HAVE_SYNC_ADD_AND_FETCH && defined HAVE_SYNC_SUB_AND_FETCH #define OF_SHARED_PTR_COUNTER_TYPE size_t #elif defined HAVE_INTERLOCKED_INCREMENT && defined HAVE_INTERLOCKED_DECREMENT -#if _MSC_VER <= 1200 -#define OF_SHARED_PTR_COUNTER_TYPE LONG -#else #define OF_SHARED_PTR_COUNTER_TYPE volatile LONG -#endif #else #define OF_SHARED_PTR_COUNTER_TYPE size_t #define OF_SHARED_PTR_NEED_MUTEX 1 diff --git a/ofstd/include/dcmtk/ofstd/ofoption.h b/ofstd/include/dcmtk/ofstd/ofoption.h index 9f53c75b..9259f1ed 100644 --- a/ofstd/include/dcmtk/ofstd/ofoption.h +++ b/ofstd/include/dcmtk/ofstd/ofoption.h @@ -33,7 +33,7 @@ // include for "std::is_default_constructible" // to recover from compiler insanity (Visual Studio 12+). -#if defined(_MSC_VER) && _MSC_VER >= 1700 +#if defined(_MSC_VER) #include #endif @@ -136,25 +136,10 @@ class OFdefault_optional_traits template static yes_type sfinae(consume*); #elif defined(_MSC_VER) -#if _MSC_VER < 1700 - // Workaround bug in Visual Studio. - // On these broken compilers, the argument is not evaluated - // unless we require them to evaluate it for choosing which - // specialization should be instantiated. - template - struct consume{}; - template - struct consume { typedef void type; }; - // sfinae overload working for value-initializable Xs, that's as - // close as we get on these broken compilers - template - static yes_type sfinae(typename consume::type*); -#else // Visual Studio 2012 is completely broken, but it has std::is_default_constructible // Note: this tests constructibility, but not if WE can construct this. template static yes_type sfinae(typename OFenable_if::value>::type*); -#endif // _MSC_VER #endif // HAVE_DEFAULT_CONSTRUCTOR_DETECTION_VIA_SFINAE // most general sfinae overload, chosen only if everything else fails template diff --git a/ofstd/include/dcmtk/ofstd/ofrand.h b/ofstd/include/dcmtk/ofstd/ofrand.h index d55a0017..27bc87b6 100644 --- a/ofstd/include/dcmtk/ofstd/ofrand.h +++ b/ofstd/include/dcmtk/ofstd/ofrand.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2018, OFFIS e.V. + * Copyright (C) 2018-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -40,12 +40,10 @@ public: /// destructor virtual ~OFRandom() {} -#ifndef OF_NO_UINT64 /** return a random Uint64 value * @return random unsigned 64-bit number */ Uint64 getRND64(); -#endif /** return a random Uint32 value * @return random unsigned 32-bit number diff --git a/ofstd/include/dcmtk/ofstd/ofsha256.h b/ofstd/include/dcmtk/ofstd/ofsha256.h new file mode 100644 index 00000000..1d80d706 --- /dev/null +++ b/ofstd/include/dcmtk/ofstd/ofsha256.h @@ -0,0 +1,71 @@ +/* + * + * Copyright (C) 2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: ofstd + * + * Author: Marco Eichelberg, based on code by Brad Conte (brad AT bradconte.com) in FreeBSD + * + * Purpose: Implementation of SHA-256 + * + */ + +#ifndef OFSHA256_H +#define OFSHA256_H + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/ofstd/ofdefine.h" +#include "dcmtk/ofstd/oftypes.h" + + +#define SHA256_BLOCK_SIZE 32 // SHA-256 outputs a 32 byte digest + +class DCMTK_OFSTD_EXPORT OFSHA256 +{ +public: + /// default constructor + OFSHA256(); + + /// initialize instance, i.e. reset to initial state + void init(); + + /** feed bytes to the hash algorithm + * @param data bytes to feed + * @param len number of bytes + */ + void update(const void *data, size_t len); + + /** finalize and return hash + * @param hash pointer to memory block of at least 32 bytes. + * The SHA-256 hash is copied into this block. + */ + void final(void *hash); + +private: + + /// transform hash key state based on the content of the data buffer + void transform(); + + /// data buffer + Uint8 data_[64]; + + /// number of bytes in data buffer + Uint32 datalen_; + + /// total number of bits that have been fed into the hash algorithm + unsigned long long bitlen_; + + /// state information for the hash key + Uint32 state_[8]; +}; + +#endif diff --git a/ofstd/include/dcmtk/ofstd/ofsockad.h b/ofstd/include/dcmtk/ofstd/ofsockad.h index 7451067b..66b56eba 100644 --- a/ofstd/include/dcmtk/ofstd/ofsockad.h +++ b/ofstd/include/dcmtk/ofstd/ofsockad.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2017-2021, OFFIS e.V. + * Copyright (C) 2017-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -35,9 +35,7 @@ END_EXTERN_C #endif BEGIN_EXTERN_C -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_SOCKET_H /* some systems (such as DEC Ultrix) don't protect from double inclusion */ #ifndef DCOMPAT_SYS_SOCKET_H_ @@ -65,7 +63,7 @@ struct sockaddr; struct sockaddr_in; struct sockaddr_in6; -/** A simple wrapper class for a struct sockaddr_storage object +/** A simple wrapper class for a struct sockaddr_storage object * that can be used to store an TCP/IPv4 (struct sockaddr_in) or TCP/IPv6 * (struct sockaddr_in6) address. */ diff --git a/ofstd/include/dcmtk/ofstd/ofstd.h b/ofstd/include/dcmtk/ofstd/ofstd.h index ace0cdba..265f51d9 100644 --- a/ofstd/include/dcmtk/ofstd/ofstd.h +++ b/ofstd/include/dcmtk/ofstd/ofstd.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2024, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -45,15 +45,15 @@ END_EXTERN_C #endif BEGIN_EXTERN_C -#ifdef HAVE_SYS_TYPES_H #include /* for size_t */ -#endif END_EXTERN_C /* Check if we are using glibc in a version where readdir() is known to be * thread-safe and where readdir_r() is deprecated. + * Android uses a different libc implementation but also guarantees that + * readdir() is thread-safe and marks readdir_r() as deprecated. */ -#if defined(__GLIBC__) && (((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 24)) || (__GLIBC__ >= 3)) +#if (defined(__GLIBC__) && (((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 24)) || (__GLIBC__ >= 3))) || defined(__ANDROID__) #define READDIR_IS_THREADSAFE #endif @@ -720,7 +720,7 @@ class DCMTK_OFSTD_EXPORT OFStandard * can be decoded back to its exact size. * If the input data is NULL an error code (EC_IllegalParameter) is returned. ** @param out output stream used for the base64 encoded data - * @param data buffer with binary data to be encoded (big endian required!) + * @param data buffer with binary data to be encoded * @param length length of the input data buffer (in bytes) * @param width maximum number of characters per line in the output stream * (default: 0 = no line breaks, typical for MIME = 72) @@ -737,7 +737,7 @@ class DCMTK_OFSTD_EXPORT OFStandard * even multiple of 3. A special character ('=') is used to denote padding so that the output * can be decoded back to its exact size. * If the input data is NULL an empty string is returned. - ** @param data buffer with binary data to be encoded (big endian required!) + ** @param data buffer with binary data to be encoded * @param length length of the input data buffer (in bytes) * @param result reference to resulting string variable (Base64 encoded) * @param width maximum number of characters per line in the output string @@ -758,7 +758,7 @@ class DCMTK_OFSTD_EXPORT OFStandard * and has to to be freed (using "delete[]") by the caller! Do not pass a pointer to an * already allocated buffer to this function, the caller does not know the exact size anyway. ** @param data Base64 encoded input data (possibly padded with '=' at the end) - * @param result receives pointer to resulting buffer with binary data (big endian encoded) + * @param result receives pointer to resulting buffer with binary data ** @return length of the resulting binary data (0 if an error occurred, in this case the buffer * is deleted internally) */ @@ -827,7 +827,7 @@ class DCMTK_OFSTD_EXPORT OFStandard /** @name ftoa() processing flags. * These flags can be combined by bit-wise or. */ - //@{ + ///@{ /// Use scientific format (equivalent to %e or %E in printf), /// instead of the default, which is equivalent to %g or %G. @@ -854,7 +854,7 @@ class DCMTK_OFSTD_EXPORT OFStandard /// pad with zeroes instead of blanks static const unsigned int ftoa_zeropad; - //@} + ///@} /** makes the current process sleep until seconds seconds have * elapsed or a signal arrives which is not ignored diff --git a/ofstd/include/dcmtk/ofstd/ofstdinc.h b/ofstd/include/dcmtk/ofstd/ofstdinc.h index bbf4fb78..03dc2f32 100644 --- a/ofstd/include/dcmtk/ofstd/ofstdinc.h +++ b/ofstd/include/dcmtk/ofstd/ofstdinc.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2021, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -37,11 +37,7 @@ using namespace std; // define STD_NAMESPACE to std:: if the standard namespace exists #ifndef STD_NAMESPACE -#ifdef HAVE_STD_NAMESPACE #define STD_NAMESPACE std:: -#else -#define STD_NAMESPACE -#endif #endif /* Print an error message in case some user code still uses the now unsupported diff --git a/ofstd/include/dcmtk/ofstd/ofstream.h b/ofstd/include/dcmtk/ofstd/ofstream.h index 05598292..9bf575b0 100644 --- a/ofstd/include/dcmtk/ofstd/ofstream.h +++ b/ofstd/include/dcmtk/ofstd/ofstream.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2024, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -44,11 +44,7 @@ // define STD_NAMESPACE to std:: if the standard namespace exists #ifndef STD_NAMESPACE -#ifdef HAVE_STD_NAMESPACE #define STD_NAMESPACE std:: -#else -#define STD_NAMESPACE -#endif #endif #define OFendl STD_NAMESPACE endl diff --git a/ofstd/include/dcmtk/ofstd/ofstrhlp.h b/ofstd/include/dcmtk/ofstd/ofstrhlp.h new file mode 100644 index 00000000..1ef8bc2d --- /dev/null +++ b/ofstd/include/dcmtk/ofstd/ofstrhlp.h @@ -0,0 +1,59 @@ +/* + * + * Copyright (C) 2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: ofstd + * + * Author: Marco Eichelberg + * + * Purpose: Helper functions to convert between std::string and OFString + * + */ + +#ifndef OFSTRHLP_H +#define OFSTRHLP_H + +#include "dcmtk/config/osconfig.h" /* include OS specific configuration first */ +#include "dcmtk/ofstd/ofstring.h" /* for class OFString */ +#include /* for std::string */ + +/** convert OFString to std::string. + * This helper function converts an OFString object into an equivalent std::string. + * @param arg string to convert + * @return string as std::string object + */ +inline STD_NAMESPACE string OFString_to_std_string(const OFString& arg) +{ +#ifdef HAVE_STL_STRING + // OFString is just a typedef for std::string + return arg; +#else + return STD_NAMESPACE string(arg.c_str(), arg.length()); +#endif +} + +/** convert std::string to OFString. + * This helper function converts a std::string object into an equivalent OFString. + * @param arg string to convert + * @return string as OFString object + */ +inline OFString std_string_to_OFString(const STD_NAMESPACE string& arg) +{ +#ifdef HAVE_STL_STRING + // OFString is just a typedef for std::string + return arg; +#else + return OFString(arg.c_str(), arg.length()); +#endif +} + +#endif /* OFSTRHLP_H */ diff --git a/ofstd/include/dcmtk/ofstd/oftempf.h b/ofstd/include/dcmtk/ofstd/oftempf.h index f0454062..2bb17334 100644 --- a/ofstd/include/dcmtk/ofstd/oftempf.h +++ b/ofstd/include/dcmtk/ofstd/oftempf.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2011-2016, OFFIS e.V. + * Copyright (C) 2011-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -29,12 +29,8 @@ #include "dcmtk/ofstd/oftypes.h" /* for OFBool */ BEGIN_EXTERN_C -#ifdef HAVE_SYS_STAT_H #include /* for O_RDWR */ -#endif -#ifdef HAVE_FCNTL_H #include /* needed on Solaris for O_RDWR */ -#endif END_EXTERN_C /** this class manages the lifetime of a temporary file. The file will be diff --git a/ofstd/include/dcmtk/ofstd/oftest.h b/ofstd/include/dcmtk/ofstd/oftest.h index fe421262..31975854 100644 --- a/ofstd/include/dcmtk/ofstd/oftest.h +++ b/ofstd/include/dcmtk/ofstd/oftest.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2011-2022, OFFIS e.V. + * Copyright (C) 2011-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This code is inspired by quicktest. @@ -514,7 +514,7 @@ public: \ * These macros can be used for doing various checks in test cases. In case * their check fails, they emit a descriptive message explaining the problem. */ -//@{ +///@{ /** Check if a condition is true. Can only be used inside OFTEST(). * @param condition condition to check @@ -561,6 +561,6 @@ public: \ OFTestManager::instance().currentTest().recordFailure(__FILE__, __LINE__, str___); \ } while (0) -//@} +///@} #endif diff --git a/ofstd/include/dcmtk/ofstd/oftime.h b/ofstd/include/dcmtk/ofstd/oftime.h index f17e18f4..d1125bb8 100644 --- a/ofstd/include/dcmtk/ofstd/oftime.h +++ b/ofstd/include/dcmtk/ofstd/oftime.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2021, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -26,9 +26,7 @@ #include "dcmtk/config/osconfig.h" BEGIN_EXTERN_C -#ifdef HAVE_SYS_TYPES_H -# include /* for struct time_t */ -#endif +#include /* for struct time_t */ END_EXTERN_C #include "dcmtk/ofstd/ofstring.h" /* for class OFString */ diff --git a/ofstd/include/dcmtk/ofstd/oftypes.h b/ofstd/include/dcmtk/ofstd/oftypes.h index 0464cf8c..ee9875f2 100644 --- a/ofstd/include/dcmtk/ofstd/oftypes.h +++ b/ofstd/include/dcmtk/ofstd/oftypes.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1997-2024, OFFIS e.V. + * Copyright (C) 1997-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -79,9 +79,7 @@ inline std::ostream& operator<<( std::ostream& o, OFnullptr_t /* unused */ ) #include BEGIN_EXTERN_C -#ifdef HAVE_STDINT_H #include -#endif #include END_EXTERN_C @@ -132,7 +130,7 @@ typedef long Sint64; typedef OFlonglong Sint64; #else /* we have not found any 64-bit signed integer type */ -#define OF_NO_SINT64 1 +#error unsupported platform (no 64-bit signed integer type) #endif #ifdef HAVE_UINT64_T @@ -146,7 +144,7 @@ typedef unsigned long Uint64; typedef OFulonglong Uint64; #else /* we have not found any 64-bit unsigned integer type */ -#define OF_NO_UINT64 1 +#error unsupported platform (no 64-bit unsigned integer type) #endif #if SIZEOF_VOID_P == 2 @@ -156,17 +154,9 @@ typedef Uint16 OFuintptr_t; typedef Sint32 OFintptr_t; typedef Uint32 OFuintptr_t; #elif SIZEOF_VOID_P == 8 -#ifndef OF_NO_SINT64 typedef Sint64 OFintptr_t; -#else -#error unsupported platform (64-Bit pointers but no 64-Bit signed integer type) -#endif -#ifndef OF_NO_UINT64 typedef Uint64 OFuintptr_t; #else -#error unsupported platform (64-Bit pointers but no 64-Bit unsigned integer type) -#endif -#else #error Unsupported platform (invalid pointer size) #endif diff --git a/ofstd/include/dcmtk/ofstd/ofvector.h b/ofstd/include/dcmtk/ofstd/ofvector.h index e4065611..ce7716df 100644 --- a/ofstd/include/dcmtk/ofstd/ofvector.h +++ b/ofstd/include/dcmtk/ofstd/ofvector.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2010-2024, OFFIS e.V. + * Copyright (C) 2010-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -28,12 +28,7 @@ // Use the standard template library (STL) vector class. #include - -#ifdef HAVE_STD_NAMESPACE #define OFVector std::vector -#else -#define OFVector vector -#endif #else diff --git a/ofstd/libsrc/CMakeLists.txt b/ofstd/libsrc/CMakeLists.txt index b705daef..cde444d5 100644 --- a/ofstd/libsrc/CMakeLists.txt +++ b/ofstd/libsrc/CMakeLists.txt @@ -1,5 +1,5 @@ # create library from source files -DCMTK_ADD_LIBRARY(ofstd +DCMTK_ADD_LIBRARY(ofstd ofchrenc.cc ofcmdln.cc ofconapp.cc @@ -14,6 +14,7 @@ DCMTK_ADD_LIBRARY(ofstd offilsys.cc offname.cc ofipc.cc + ofjsmn.cc oflist.cc ofstd.cc ofstring.cc @@ -29,6 +30,7 @@ DCMTK_ADD_LIBRARY(ofstd ofrand.cc ofwhere.c ofstub.cc + ofsha256.cc ) DCMTK_TARGET_LINK_LIBRARIES(ofstd config ${CHARSET_CONVERSION_LIBS} ${SOCKET_LIBS} ${THREAD_LIBS} ${WIN32_STD_LIBRARIES}) diff --git a/ofstd/libsrc/Makefile.dep b/ofstd/libsrc/Makefile.dep index 4bcc3721..54042dfc 100644 --- a/ofstd/libsrc/Makefile.dep +++ b/ofstd/libsrc/Makefile.dep @@ -142,6 +142,11 @@ ofipc.o: ofipc.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/ofstd/oflist.h ../include/dcmtk/ofstd/ofstd.h \ ../include/dcmtk/ofstd/oftraits.h ../include/dcmtk/ofstd/oflimits.h \ ../include/dcmtk/ofstd/oferror.h +ofjsmn.o: ofjsmn.cc ../include/dcmtk/ofstd/ofjsmn.h \ + ../../config/include/dcmtk/config/osconfig.h \ + ../include/dcmtk/ofstd/oftypes.h ../include/dcmtk/ofstd/ofdefine.h \ + ../include/dcmtk/ofstd/ofcast.h ../include/dcmtk/ofstd/ofexport.h \ + ../include/dcmtk/ofstd/ofstdinc.h oflist.o: oflist.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/ofstd/oflist.h ../include/dcmtk/ofstd/oftypes.h \ ../include/dcmtk/ofstd/ofdefine.h ../include/dcmtk/ofstd/ofcast.h \ @@ -163,6 +168,10 @@ ofrand.o: ofrand.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/ofstd/diag/useafree.def \ ../include/dcmtk/ofstd/diag/pop.def ../include/dcmtk/ofstd/oflimits.h \ ../include/dcmtk/ofstd/oferror.h +ofsha256.o: ofsha256.cc ../../config/include/dcmtk/config/osconfig.h \ + ../include/dcmtk/ofstd/ofsha256.h ../include/dcmtk/ofstd/ofdefine.h \ + ../include/dcmtk/ofstd/ofcast.h ../include/dcmtk/ofstd/ofexport.h \ + ../include/dcmtk/ofstd/oftypes.h ../include/dcmtk/ofstd/ofstdinc.h ofsockad.o: ofsockad.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/ofstd/ofsockad.h ../include/dcmtk/ofstd/ofdefine.h \ ../include/dcmtk/ofstd/ofcast.h ../include/dcmtk/ofstd/ofexport.h \ diff --git a/ofstd/libsrc/Makefile.in b/ofstd/libsrc/Makefile.in index bb720dc9..50a1a6c2 100644 --- a/ofstd/libsrc/Makefile.in +++ b/ofstd/libsrc/Makefile.in @@ -21,7 +21,7 @@ objs = oflist.o ofstring.o ofcmdln.o ofconapp.o offname.o ofconsol.o ofthread.o ofcond.o ofstd.o ofcrc32.o ofdate.o oftime.o ofdatime.o oftimer.o \ ofconfig.o ofchrenc.o oftempf.o ofxml.o ofuuid.o offile.o offilsys.o \ ofmath.o oferror.o ofsockad.o ofrand.o ofstrutl.o ofipc.o ofwhere.o \ - ofstub.o + ofstub.o ofsha256.o ofjsmn.o library = libofstd.$(LIBEXT) diff --git a/ofstd/libsrc/ofconsol.cc b/ofstd/libsrc/ofconsol.cc index 21b3b86b..d2d80e8e 100644 --- a/ofstd/libsrc/ofconsol.cc +++ b/ofstd/libsrc/ofconsol.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2000-2021, OFFIS e.V. + * Copyright (C) 2000-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -32,9 +32,7 @@ END_EXTERN_C BEGIN_EXTERN_C -#ifdef HAVE_FCNTL_H #include -#endif #ifdef HAVE_IO_H #include #endif @@ -154,7 +152,7 @@ void OFConsole::mergeStderrStdout() } } -#ifndef __BORLANDC__ /* setvbuf on stdout/stderr does not work with Borland C++ */ +#ifndef HAVE_CLASSIC_BORLAND_COMPILER /* setvbuf on stdout/stderr does not work with Borland C++ */ /* set stdout and stderr to unbuffered mode */ if (setvbuf(stdout, NULL, _IONBF, 0 ) != 0 ) { @@ -166,7 +164,7 @@ void OFConsole::mergeStderrStdout() OFConsole::instance().lockCerr() << "Unable to switch stderr to unbuffered mode" << OFendl; OFConsole::instance().unlockCerr(); } -#endif /* __BORLANDC__ */ +#endif /* HAVE_CLASSIC_BORLAND_COMPILER */ } @@ -181,7 +179,7 @@ void OFConsole::unmergeStderrStdout() OFConsole::instance().unlockCerr(); } -#ifndef __BORLANDC__ +#ifndef HAVE_CLASSIC_BORLAND_COMPILER /* switch stdout to buffered mode */ if (setvbuf(stdout, NULL, _IOFBF, BUFSIZ ) != 0 ) { @@ -189,7 +187,7 @@ void OFConsole::unmergeStderrStdout() OFConsole::instance().unlockCerr(); } -#endif /* __BORLANDC__ */ +#endif /* HAVE_CLASSIC_BORLAND_COMPILER */ } } diff --git a/ofstd/libsrc/ofdatime.cc b/ofstd/libsrc/ofdatime.cc index 308167a8..8ffb171f 100644 --- a/ofstd/libsrc/ofdatime.cc +++ b/ofstd/libsrc/ofdatime.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2002-2021, OFFIS e.V. + * Copyright (C) 2002-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -22,11 +22,9 @@ #include "dcmtk/config/osconfig.h" -#ifdef HAVE_SYS_TYPES_H BEGIN_EXTERN_C #include /* for struct time_t */ END_EXTERN_C -#endif #include "dcmtk/ofstd/ofdatime.h" #include "dcmtk/ofstd/ofstdinc.h" diff --git a/ofstd/libsrc/offilsys.cc b/ofstd/libsrc/offilsys.cc index 1cc96590..d51dc4cc 100644 --- a/ofstd/libsrc/offilsys.cc +++ b/ofstd/libsrc/offilsys.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2021-2024, OFFIS e.V. + * Copyright (C) 2021-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -31,9 +31,7 @@ #include #else // _WIN32 BEGIN_EXTERN_C -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef HAVE_SYS_FILE_H #include // for struct DIR, opendir() #endif @@ -43,8 +41,10 @@ BEGIN_EXTERN_C END_EXTERN_C /* Check if we are using glibc in a version where readdir() is known to be * thread-safe and where readdir_r() is deprecated. + * Android uses a different libc implementation but also guarantees that + * readdir() is thread-safe and marks readdir_r() as deprecated. */ -#if defined(__GLIBC__) && (((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 24)) || (__GLIBC__ >= 3)) +#if (defined(__GLIBC__) && (((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 24)) || (__GLIBC__ >= 3))) || defined(__ANDROID__) #define READDIR_IS_THREADSAFE #endif #endif // _WIN32 diff --git a/ofstd/libsrc/offname.cc b/ofstd/libsrc/offname.cc index a56a7e1b..cf14d0b7 100644 --- a/ofstd/libsrc/offname.cc +++ b/ofstd/libsrc/offname.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1997-2022, OFFIS e.V. + * Copyright (C) 1997-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -30,12 +30,8 @@ BEGIN_EXTERN_C -#ifdef HAVE_SYS_TYPES_H #include /* for time_t */ -#endif -#ifdef HAVE_SYS_STAT_H #include /* for stat() */ -#endif END_EXTERN_C /* give up after this number of unsuccessful attempts to create a unique filename */ diff --git a/ofstd/libsrc/ofipc.cc b/ofstd/libsrc/ofipc.cc index 2ecbb76c..ff9ebff2 100644 --- a/ofstd/libsrc/ofipc.cc +++ b/ofstd/libsrc/ofipc.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2022-2024, OFFIS e.V. + * Copyright (C) 2022-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -38,18 +38,12 @@ BEGIN_EXTERN_C #if defined(HAVE_MQUEUE_H) && !defined(__FreeBSD__) #include #endif -#ifdef HAVE_FCNTL_H #include -#endif -#ifdef HAVE_SYS_STAT_H #include -#endif #ifdef HAVE_SYS_MSG_H #include #endif -#ifdef HAVE_SYS_TYPES_H #include -#endif #ifdef DCMTK_HAVE_POLL #include diff --git a/ofstd/libsrc/ofjsmn.cc b/ofstd/libsrc/ofjsmn.cc new file mode 100644 index 00000000..31deecb5 --- /dev/null +++ b/ofstd/libsrc/ofjsmn.cc @@ -0,0 +1,22 @@ +/* + * + * Copyright (C) 2024-2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: ofstd + * + * Author: Tingyan Xu + * + * Purpose: Implementation of JSON parser library + * + */ + +#include "dcmtk/ofstd/ofjsmn.h" diff --git a/ofstd/libsrc/ofmath.cc b/ofstd/libsrc/ofmath.cc index 927cc132..790fb16b 100644 --- a/ofstd/libsrc/ofmath.cc +++ b/ofstd/libsrc/ofmath.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2015-2016, OFFIS e.V. + * Copyright (C) 2015-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -45,3 +45,8 @@ OFBool OFMath::isinf( double d ) { return (dcmtk_config_math::isinf)( d ); } + +double OFMath::sqrt(double d) +{ + return (dcmtk_config_math::sqrt)( d ); +} diff --git a/ofstd/libsrc/ofrand.cc b/ofstd/libsrc/ofrand.cc index 45401c4d..563bb0d3 100644 --- a/ofstd/libsrc/ofrand.cc +++ b/ofstd/libsrc/ofrand.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2021, OFFIS e.V. + * Copyright (C) 2021-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -186,7 +186,6 @@ Uint16 OFRandom::getRND16() return OFstatic_cast(Uint16, getRND32()); } -#ifndef OF_NO_UINT64 Uint64 OFRandom::getRND64() { // get a 32-bit random number @@ -197,4 +196,3 @@ Uint64 OFRandom::getRND64() result |= getRND32(); return result; } -#endif diff --git a/ofstd/libsrc/ofsha256.cc b/ofstd/libsrc/ofsha256.cc new file mode 100644 index 00000000..b18d6c2c --- /dev/null +++ b/ofstd/libsrc/ofsha256.cc @@ -0,0 +1,169 @@ +/* + * + * Copyright (C) 2025, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: ofstd + * + * Author: Marco Eichelberg, based on code by Brad Conte (brad AT bradconte.com) in FreeBSD. + * Disclaimer: This code is presented "as is" without any guarantees. + * Details: Implementation of the SHA-256 hashing algorithm. + * SHA-256 is one of the three algorithms in the SHA2 + * specification. The others, SHA-384 and SHA-512, are not + * offered in this implementation. + * Algorithm specification can be found here: + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf + * This implementation internally uses little endian byte order. + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/ofstd/ofsha256.h" +#include + +#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) + +static const Uint32 k[64] = { + 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, + 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, + 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, + 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, + 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, + 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, + 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, + 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +}; + + +void OFSHA256::transform() +{ + Uint32 a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (OFstatic_cast(Uint32, data_[j]) << 24) | (OFstatic_cast(Uint32, data_[j + 1]) << 16) | (OFstatic_cast(Uint32, data_[j + 2]) << 8) | (OFstatic_cast(Uint32, data_[j + 3])); + for ( ; i < 64; ++i) + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + + a = state_[0]; + b = state_[1]; + c = state_[2]; + d = state_[3]; + e = state_[4]; + f = state_[5]; + g = state_[6]; + h = state_[7]; + + for (i = 0; i < 64; ++i) { + t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; + t2 = EP0(a) + MAJ(a,b,c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + state_[0] += a; + state_[1] += b; + state_[2] += c; + state_[3] += d; + state_[4] += e; + state_[5] += f; + state_[6] += g; + state_[7] += h; +} + + +OFSHA256::OFSHA256() +{ + init(); +} + +void OFSHA256::init() +{ + datalen_ = 0; + bitlen_ = 0; + state_[0] = 0x6a09e667; + state_[1] = 0xbb67ae85; + state_[2] = 0x3c6ef372; + state_[3] = 0xa54ff53a; + state_[4] = 0x510e527f; + state_[5] = 0x9b05688c; + state_[6] = 0x1f83d9ab; + state_[7] = 0x5be0cd19; +} + +void OFSHA256::update(const void *data, size_t len) +{ + if (data == NULL) return; + const Uint8 *uint8data = OFreinterpret_cast(const Uint8 *, data); + for (size_t i = 0; i < len; ++i) { + data_[datalen_] = uint8data[i]; + datalen_++; + if (datalen_ == 64) { + transform(); + bitlen_ += 512; + datalen_ = 0; + } + } +} + +void OFSHA256::final(void *hash) +{ + if (hash == NULL) return; + + Uint8 *uint8hash = OFreinterpret_cast(Uint8 *, hash); + Uint32 i, j; + i = datalen_; + + // Pad whatever data is left in the buffer. + if (datalen_ < 56) { + data_[i++] = 0x80; + while (i < 56) + data_[i++] = 0x00; + } + else { + data_[i++] = 0x80; + while (i < 64) + data_[i++] = 0x00; + transform(); + memset(data_, 0, 56); + } + + // Append to the padding the total message's length in bits and transform. + bitlen_ += datalen_ * 8; + data_[63] = OFstatic_cast(Uint8, bitlen_); + data_[62] = OFstatic_cast(Uint8,(bitlen_ >> 8)); + data_[61] = OFstatic_cast(Uint8,(bitlen_ >> 16)); + data_[60] = OFstatic_cast(Uint8,(bitlen_ >> 24)); + data_[59] = OFstatic_cast(Uint8,(bitlen_ >> 32)); + data_[58] = OFstatic_cast(Uint8,(bitlen_ >> 40)); + data_[57] = OFstatic_cast(Uint8,(bitlen_ >> 48)); + data_[56] = OFstatic_cast(Uint8,(bitlen_ >> 56)); + transform(); + + // Since this implementation uses little endian byte ordering and SHA uses big endian, + // reverse all the bytes when copying the final state to the output hash. + for (i = j = 0; i < 8; ++i, ++j) { + uint8hash[j++] = (state_[i] >> 24) & 0xff; + uint8hash[j++] = (state_[i] >> 16) & 0xff; + uint8hash[j++] = (state_[i] >> 8) & 0xff; + uint8hash[j] = state_[i] & 0xff; + } +} diff --git a/ofstd/libsrc/ofstd.cc b/ofstd/libsrc/ofstd.cc index 748932ad..3524695a 100644 --- a/ofstd/libsrc/ofstd.cc +++ b/ofstd/libsrc/ofstd.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2001-2024, OFFIS e.V. + * Copyright (C) 2001-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -121,15 +121,11 @@ END_EXTERN_C #include BEGIN_EXTERN_C -#ifdef HAVE_SYS_STAT_H #include /* for stat() */ -#endif #ifdef HAVE_IO_H #include /* for access() on Win32 */ #endif -#ifdef HAVE_SYS_TYPES_H #include /* for opendir() and closedir() */ -#endif #ifdef HAVE_DIRENT_H #include /* for opendir() and closedir() */ #else @@ -225,8 +221,9 @@ END_EXTERN_C #if !defined(ENABLE_OLD_OFSTD_FTOA_IMPLEMENTATION) && !defined(ENABLE_IOSTREAM_BASED_FTOA_IMPLEMENTATION) && !defined(ENABLE_CSTDIO_BASED_FTOA_IMPLEMENTATION) #ifdef _WIN32 // on Windows, the iostream-based implementation of atof is extremely slow, -// and we do have a locale independent version of sscanf. Use this version. -#define ENABLE_CSTDIO_BASED_ATOF_IMPLEMENTATION +// and we do have a locale independent version of sprintf, called _snprintf_s_l. +// Use this version. +#define ENABLE_CSTDIO_BASED_FTOA_IMPLEMENTATION #else // on other platforms, we assume that the iobased-implementation, being the // cleanest one, is appropriate. This is known to be the case for gcc and clang with glibc. @@ -349,37 +346,7 @@ int OFStandard::vsnprintf(char *str, size_t size, const char *format, va_list ap return ::vsnprintf(str, size, format, ap); #endif /* _MSC_VER < 1900 */ #else /* _MSC_VER */ -#ifdef HAVE_VSNPRINTF return ::vsnprintf(str, size, format, ap); -#else /* HAVE_VSNPRINTF */ -#ifdef DCMTK_ENABLE_UNSAFE_VSNPRINTF - // This implementation internally uses vsprintf (which is inherently unsafe). - // It allocates a buffer that is 1 kByte larger than "size", - // formats the string into that buffer, and then uses strlcpy() to - // copy the formatted string into the output buffer, truncating if necessary. - // This will work in most cases, since few snprintf calls should overrun - // the provided buffer by more than 1K, but it can be easily abused by - // a malicious attacker to cause a buffer overrun. - // - // Therefore, this implementation should only be used as a "last resort" - // and we strongly advise against using it in production code. - // The macro "DCMTK_ENABLE_UNSAFE_VSNPRINTF" must explicitly be defined - // by the used to enable this implementation. - int count = -1; - if (size != 0) - { - char *buf = new char[size+1024]; - count = ::vsprintf(buf, format, ap); - OFStandard::strlcpy(str, buf, size); - delete[] buf; - } - return count; -#warning Using unsafe implementation of vsnprintf(3) -#else /* DCMTK_ENABLE_UNSAFE_VSNPRINTF */ - return -1; -#error vsnprintf(3) not found. Use different compiler or compile with DCMTK_ENABLE_UNSAFE_VSNPRINTF (unsafe!) -#endif /* DCMTK_ENABLE_UNSAFE_VSNPRINTF */ -#endif /* HAVE_VSNPRINTF */ #endif /* _MSC_VER */ } @@ -472,7 +439,6 @@ OFBool OFStandard::pathExists(const OFFilename &pathName) /* check for valid path name (avoid NULL or empty string) */ if (!pathName.isEmpty()) { -#ifdef HAVE_ACCESS /* check existence with "access()" */ #if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32) /* check whether to use the wide-char version of the API function */ @@ -481,31 +447,6 @@ OFBool OFStandard::pathExists(const OFFilename &pathName) else #endif result = (access(pathName.getCharPointer(), F_OK) == 0); -#else /* HAVE_ACCESS */ -#ifdef HAVE_WINDOWS_H - /* get file attributes */ - DWORD fileAttr; -#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32) - /* check whether to use the wide-char version of the API function */ - if (pathName.usesWideChars()) - fileAttr = GetFileAttributesW(pathName.getWideCharPointer()); - else -#endif - fileAttr = GetFileAttributes(pathName.getCharPointer()); - result = (fileAttr != 0xffffffff); -#else /* HAVE_WINDOWS_H */ -#ifdef HAVE_SYS_STAT_H - /* check existence with "stat()" */ - struct stat stat_buf; - result = (stat(pathName.getCharPointer(), &stat_buf) == 0); -#else - /* try to open the given "file" (or directory) in read-only mode */ - OFFile file; - result = file.fopen(pathName, "r"); - file.fclose(); -#endif /* HAVE_SYS_STAT_H */ -#endif /* HAVE_WINDOWS_H */ -#endif /* HAVE_ACCESS */ } return result; } @@ -582,7 +523,6 @@ OFBool OFStandard::isReadable(const OFFilename &pathName) /* check for valid path name (avoid NULL or empty string) */ if (!pathName.isEmpty()) { -#ifdef HAVE_ACCESS /* check whether the path is readable using "access()" */ #if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32) /* check whether to use the wide-char version of the API function */ @@ -591,11 +531,6 @@ OFBool OFStandard::isReadable(const OFFilename &pathName) else #endif result = (access(pathName.getCharPointer(), R_OK) == 0); -#else /* HAVE_ACCESS */ - /* try to open the given "file" (or directory) in read-only mode */ - OFFile file; - result = file.fopen(pathName, "r"); -#endif /* HAVE_ACCESS */ } return result; } @@ -607,7 +542,6 @@ OFBool OFStandard::isWriteable(const OFFilename &pathName) /* check for valid path name (avoid NULL or empty string) */ if (!pathName.isEmpty()) { -#ifdef HAVE_ACCESS /* check whether the path is writable using "access()" */ #if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32) /* check whether to use the wide-char version of the API function */ @@ -616,11 +550,6 @@ OFBool OFStandard::isWriteable(const OFFilename &pathName) else #endif result = (access(pathName.getCharPointer(), W_OK) == 0); -#else /* HAVE_ACCESS */ - /* try to open the given "file" (or directory) in write mode */ - OFFile file; - result = file.fopen(pathName, "w"); -#endif /* HAVE_ACCESS */ } return result; } @@ -1989,6 +1918,13 @@ double OFStandard::atof(const char *s, OFBool *success) return OFnumeric_limits::quiet_NaN(); } + // handle negative NaN as a special case, since iostream does not + if ((ss.length() >= 4) && (ss[0] == '-') && (ss[1] == 'n' || ss[1] == 'N') && (ss[2] == 'a' || ss[2] == 'A') && (ss[3] == 'n' || ss[3] == 'N')) + { + if (success) *success = OFTrue; + return OFnumeric_limits::quiet_NaN(); + } + // handle positive infinity as a special case, since iostream does not if ((ss.length() >= 3) && (ss[0] == 'i' || ss[0] == 'I') && (ss[1] == 'n' || ss[1] == 'N') && (ss[2] == 'f' || ss[2] == 'F')) { @@ -2017,6 +1953,9 @@ double OFStandard::atof(const char *s, OFBool *success) #else /* ENABLE_IOSTREAM_BASED_ATOF_IMPLEMENTATION */ +// This is the implementation in use when ENABLE_CSTDIO_BASED_ATOF_IMPLEMENTATION +// is defined. + #ifdef _WIN32 // Windows has a sscanf version where we can explicitly pass a locale @@ -2435,6 +2374,9 @@ static void ftoa_convert( #else /* ENABLE_IOSTREAM_BASED_FTOA_IMPLEMENTATION */ +// This is the implementation in use when ENABLE_CSTDIO_BASED_FTOA_IMPLEMENTATION +// is defined. + static void ftoa_convert( char *dst, size_t siz, @@ -2527,7 +2469,7 @@ void OFStandard::ftoa( if (!success || d != val) { // really need precision 17 (DBL_DECIMAL_DIG) - ftoa_convert(dst, siz, val, flags, width, prec); + ftoa_convert(dst, siz, val, flags, width, 17); } } else @@ -3073,10 +3015,8 @@ long OFStandard::getProcessID() { #ifdef _WIN32 return _getpid(); -#elif defined(HAVE_GETPID) - return getpid(); #else - return 0; // Workaround for MAC + return getpid(); #endif } diff --git a/ofstd/libsrc/ofstub.cc b/ofstd/libsrc/ofstub.cc index c1df7068..f1685de8 100644 --- a/ofstd/libsrc/ofstub.cc +++ b/ofstd/libsrc/ofstub.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2024, OFFIS e.V. + * Copyright (C) 2024-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -141,6 +141,11 @@ static OFString getLastErrorString() return OFString(); } +#elif defined(__APPLE__) + +#include +#define environ (*_NSGetEnviron()) + #else /* _WIN32 */ extern char** environ; // required to exist by the Single Unix Specification @@ -156,16 +161,42 @@ int OFstub_main(int argc, char** argv, const char *stubName, const char *appName return EXITCODE_ILLEGAL_PARAMS; } + long len = 43 - OFstatic_cast(long, strlen(stubName)) - OFstatic_cast(long, strlen(appName)); + if (len < 0) len = 0; + #ifdef DCMTK_USE_OFLOG_LOGGER_IN_STUB OFString logger_name = "dcmtk.apps."; logger_name += stubName; OFLogger logger = OFLog::getLogger(logger_name.c_str()); -#endif -#ifdef DCMTK_USE_OFLOG_LOGGER_IN_STUB - OFLOG_WARN(logger, stubName << " is deprecated, use " << appName << " instead"); + OFString output = + "##########################################################################\n" + "# #\n#"; + output.append(len/2, ' '); + output.append(stubName); + output.append(" is deprecated, use "); + output.append(appName); + output.append(" instead!"); + output.append(len - (len/2), ' '); + output.append("#\n" + "# #\n" + "##########################################################################\n"); + OFLOG_WARN(logger, output); #else - fprintf(stderr, "W: %s is deprecated, use %s instead\n", stubName, appName); + OFString output = + "W: ##########################################################################\n" + "W: # #\nW: #"; + output.append(len/2, ' '); + output.append(stubName); + output.append(" is deprecated, use "); + output.append(appName); + output.append(" instead!"); + output.append(len - (len/2), ' '); + output.append("#\n" + "W: # #\n" + "W: ##########################################################################\n\n"); + + fprintf(stderr, "%s", output.c_str()); #endif // get real path in which the stub executable is located diff --git a/ofstd/libsrc/ofxml.cc b/ofstd/libsrc/ofxml.cc index c3904d29..d93b6116 100644 --- a/ofstd/libsrc/ofxml.cc +++ b/ofstd/libsrc/ofxml.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2011-2023, OFFIS e.V. + * Copyright (C) 2011-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were slightly modified by @@ -328,7 +328,7 @@ char myIsTextWideChar(const void * /*b*/, int /*len*/) { return FALSE; } } static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return fopen(filename,mode); } static inline int xstrlen(XMLCSTR c) { return OFstatic_cast(int, strlen(c)); } - #ifdef __BORLANDC__ + #ifdef HAVE_CLASSIC_BORLAND_COMPILER static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return strnicmp(c1,c2,l);} static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return stricmp(c1,c2); } #else diff --git a/ofstd/tests/Makefile.dep b/ofstd/tests/Makefile.dep index 4626df06..061ab129 100644 --- a/ofstd/tests/Makefile.dep +++ b/ofstd/tests/Makefile.dep @@ -306,13 +306,14 @@ tstring.o: tstring.cc ../../config/include/dcmtk/config/osconfig.h \ ../include/dcmtk/ofstd/ofstring.h ../include/dcmtk/ofstd/oftypes.h \ ../include/dcmtk/ofstd/ofdefine.h ../include/dcmtk/ofstd/ofcast.h \ ../include/dcmtk/ofstd/ofexport.h ../include/dcmtk/ofstd/ofstdinc.h \ - ../include/dcmtk/ofstd/ofstream.h ../include/dcmtk/ofstd/oftest.h \ - ../include/dcmtk/ofstd/ofconapp.h ../include/dcmtk/ofstd/ofcmdln.h \ - ../include/dcmtk/ofstd/ofexbl.h ../include/dcmtk/ofstd/oftraits.h \ - ../include/dcmtk/ofstd/oflist.h ../include/dcmtk/ofstd/ofconsol.h \ - ../include/dcmtk/ofstd/ofthread.h ../include/dcmtk/ofstd/offile.h \ - ../include/dcmtk/ofstd/ofstd.h ../include/dcmtk/ofstd/ofcond.h \ - ../include/dcmtk/ofstd/ofdiag.h ../include/dcmtk/ofstd/diag/push.def \ + ../include/dcmtk/ofstd/ofstream.h ../include/dcmtk/ofstd/ofstrhlp.h \ + ../include/dcmtk/ofstd/oftest.h ../include/dcmtk/ofstd/ofconapp.h \ + ../include/dcmtk/ofstd/ofcmdln.h ../include/dcmtk/ofstd/ofexbl.h \ + ../include/dcmtk/ofstd/oftraits.h ../include/dcmtk/ofstd/oflist.h \ + ../include/dcmtk/ofstd/ofconsol.h ../include/dcmtk/ofstd/ofthread.h \ + ../include/dcmtk/ofstd/offile.h ../include/dcmtk/ofstd/ofstd.h \ + ../include/dcmtk/ofstd/ofcond.h ../include/dcmtk/ofstd/ofdiag.h \ + ../include/dcmtk/ofstd/diag/push.def \ ../include/dcmtk/ofstd/diag/useafree.def \ ../include/dcmtk/ofstd/diag/pop.def ../include/dcmtk/ofstd/oflimits.h \ ../include/dcmtk/ofstd/oferror.h ../include/dcmtk/ofstd/ofexit.h diff --git a/ofstd/tests/tests.cc b/ofstd/tests/tests.cc index b187e277..33398b21 100644 --- a/ofstd/tests/tests.cc +++ b/ofstd/tests/tests.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2011-2024, OFFIS e.V. + * Copyright (C) 2011-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -52,6 +52,7 @@ OFTEST_REGISTER(ofstd_OFFile); OFTEST_REGISTER(ofstd_OFString_compare); OFTEST_REGISTER(ofstd_OFString_concatenate); OFTEST_REGISTER(ofstd_OFString_constructor); +OFTEST_REGISTER(ofstd_OFString_conversion); OFTEST_REGISTER(ofstd_OFString_copy); OFTEST_REGISTER(ofstd_OFString_identity_1); OFTEST_REGISTER(ofstd_OFString_identity_2); diff --git a/ofstd/tests/tstring.cc b/ofstd/tests/tstring.cc index ebed9df8..f36d921f 100644 --- a/ofstd/tests/tstring.cc +++ b/ofstd/tests/tstring.cc @@ -1,6 +1,6 @@ /* * - * Copyright (C) 1997-2019, OFFIS e.V. + * Copyright (C) 1997-2025, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -37,6 +37,7 @@ #include "dcmtk/config/osconfig.h" /* include OS specific configuration first */ #include "dcmtk/ofstd/ofstring.h" +#include "dcmtk/ofstd/ofstrhlp.h" #define OFTEST_OFSTD_ONLY #include "dcmtk/ofstd/oftest.h" @@ -295,3 +296,36 @@ OFTEST(ofstd_OFString_identity_3) { identitytest(X+Y+N+X+Y+N, "A string that will be used in identitytest but is otherwise just another useless string."); } + +OFTEST(ofstd_OFString_conversion) +{ + // convert an OFString to std::string + OFString a("ABC"); + STD_NAMESPACE string b = OFString_to_std_string(a); + OFCHECK_EQUAL(b.length(), 3); + OFCHECK_EQUAL(b.at(0), 'A'); + OFCHECK_EQUAL(b.at(1), 'B'); + OFCHECK_EQUAL(b.at(2), 'C'); + + // convert an OFString containing a null byte to std::string + a.at(1)= '\0'; + STD_NAMESPACE string cc = OFString_to_std_string(a); + OFCHECK_EQUAL(cc.length(), 3); + OFCHECK_EQUAL(cc.at(0), 'A'); + OFCHECK_EQUAL(cc.at(1), '\0'); + OFCHECK_EQUAL(cc.at(2), 'C'); + + // convert a std::string to OFString + OFString d = std_string_to_OFString(b); + OFCHECK_EQUAL(d.length(), 3); + OFCHECK_EQUAL(d.at(0), 'A'); + OFCHECK_EQUAL(d.at(1), 'B'); + OFCHECK_EQUAL(d.at(2), 'C'); + + // convert a std::string containing a null byte to OFString + OFString e = std_string_to_OFString(cc); + OFCHECK_EQUAL(e.length(), 3); + OFCHECK_EQUAL(e.at(0), 'A'); + OFCHECK_EQUAL(e.at(1), '\0'); + OFCHECK_EQUAL(e.at(2), 'C'); +} -- 2.30.2